Changeset 6873
- Timestamp:
- Apr 17, 2006, 8:10:08 AM (20 years ago)
- Location:
- trunk/psModules/src
- Files:
-
- 21 edited
-
astrom/pmAstrometry.c (modified) (1 diff)
-
astrom/pmAstrometry.h (modified) (1 diff)
-
astrom/pmAstrometryObjects.h (modified) (1 diff)
-
config/pmConfig.c (modified) (22 diffs)
-
config/pmConfig.h (modified) (1 diff)
-
detrend/pmFlatField.c (modified) (1 diff)
-
detrend/pmMaskBadPixels.c (modified) (1 diff)
-
detrend/pmMaskBadPixelsErrors.h (modified) (1 diff)
-
detrend/pmNonLinear.c (modified) (1 diff)
-
imcombine/pmReadoutCombine.c (modified) (5 diffs)
-
imcombine/pmReadoutCombine.h (modified) (1 diff)
-
imsubtract/pmSubtractBias.c (modified) (13 diffs)
-
imsubtract/pmSubtractBias.h (modified) (1 diff)
-
imsubtract/pmSubtractSky.h (modified) (1 diff)
-
objects/pmModelGroup.c (modified) (1 diff)
-
objects/pmModelGroup.h (modified) (1 diff)
-
objects/pmObjects.c (modified) (2 diffs)
-
objects/pmPSF.c (modified) (8 diffs)
-
objects/pmPSF.h (modified) (1 diff)
-
objects/pmPSFtry.c (modified) (13 diffs)
-
objects/pmPSFtry.h (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/psModules/src/astrom/pmAstrometry.c
r6872 r6873 13 13 * XXX: Should we implement non-linear cell->chip transforms? 14 14 * 15 * @version $Revision: 1.1 3$ $Name: not supported by cvs2svn $16 * @date $Date: 2006-04-17 18: 01:04$15 * @version $Revision: 1.14 $ $Name: not supported by cvs2svn $ 16 * @date $Date: 2006-04-17 18:10:08 $ 17 17 * 18 18 * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii -
trunk/psModules/src/astrom/pmAstrometry.h
r6872 r6873 8 8 * @author GLG, MHPCC 9 9 * 10 * @version $Revision: 1. 7$ $Name: not supported by cvs2svn $11 * @date $Date: 2006-04-17 18: 01:04$10 * @version $Revision: 1.8 $ $Name: not supported by cvs2svn $ 11 * @date $Date: 2006-04-17 18:10:08 $ 12 12 * 13 13 * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii -
trunk/psModules/src/astrom/pmAstrometryObjects.h
r6872 r6873 8 8 * @author EAM, IfA 9 9 * 10 * @version $Revision: 1. 3$ $Name: not supported by cvs2svn $11 * @date $Date: 2006-04-17 18: 01:04$10 * @version $Revision: 1.4 $ $Name: not supported by cvs2svn $ 11 * @date $Date: 2006-04-17 18:10:08 $ 12 12 * 13 13 * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii -
trunk/psModules/src/config/pmConfig.c
r6418 r6873 3 3 * @author PAP, IfA 4 4 * 5 * @version $Revision: 1. 9$ $Name: not supported by cvs2svn $6 * @date $Date: 2006-0 2-10 02:43:19$5 * @version $Revision: 1.10 $ $Name: not supported by cvs2svn $ 6 * @date $Date: 2006-04-17 18:10:08 $ 7 7 * 8 8 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii … … 11 11 #include <stdio.h> 12 12 #include <string.h> 13 #include <unistd.h> 14 #include <assert.h> 15 #include <sys/types.h> 16 #include <sys/stat.h> 17 #include <glob.h> 13 18 #include "pslib.h" 14 19 #include "pmConfig.h" 15 20 16 #define PS_SITE "PS_SITE" // Name of the environment variable containing the site config file 17 #define PS_DEFAULT_SITE "ipprc.config" // Default site config file 21 #define PS_SITE "PS_SITE" // Name of the environment variable containing the site config file 22 #define PS_DEFAULT_SITE ".ipprc" // Default site config file 23 24 static psArray *configPath = NULL; 25 26 static void configFree(pmConfig *config) 27 { 28 psFree(config->site); 29 psFree(config->files); 30 psFree(config->camera); 31 psFree(config->recipes); 32 psFree(config->arguments); 33 psFree(config->database); 34 } 35 36 pmConfig *pmConfigAlloc(void) 37 { 38 pmConfig *config = psAlloc(sizeof(pmConfig)); 39 (void)psMemSetDeallocator(config, (psFreeFunc)configFree); 40 41 // Initialise 42 config->site = NULL; 43 config->camera = NULL; 44 config->recipes = NULL; 45 config->arguments = NULL; 46 config->database = NULL; 47 48 // the file structure is used to carry pmFPAfiles 49 config->files = psMetadataAlloc (); 50 return config; 51 } 52 53 void pmConfigDone(void) 54 { 55 psFree(configPath); 56 } 57 18 58 19 59 /** readConfig … … 23 63 * 24 64 */ 25 staticbool readConfig(65 bool readConfig( 26 66 psMetadata **config, // Config to output 27 67 const char *name, // Name of file 28 68 const char *description) // Description of file 29 69 { 70 char *realName = NULL; 30 71 unsigned int numBadLines = 0; 72 struct stat filestat; 31 73 32 74 psLogMsg(__func__, PS_LOG_INFO, "Loading %s configuration from file %s\n", 33 75 description, name); 34 *config = psMetadataConfigParse(NULL, &numBadLines, name, true); 76 77 uid_t uid = getuid(); 78 gid_t gid = getgid(); 79 80 // we try: name, path[0]/name, path[1]/name, ... 81 // find the first existing entry in the path (starting with the bare name) 82 realName = psStringCopy (name); 83 psTrace (__func__, 5, "trying %s\n", realName); 84 85 int status = stat (realName, &filestat); 86 if (status == 0) { 87 if ((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR)) 88 goto found; 89 if ((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP)) 90 goto found; 91 if (filestat.st_mode & S_IROTH) 92 goto found; 93 } 94 psFree (realName); 95 96 if (configPath == NULL) { 97 psError(PS_ERR_IO, false, "Cannot find %s configuration file in path\n", description); 98 return false; 99 } 100 101 for (int i = 0; i < configPath->n; i++) { 102 realName = psStringCopy (configPath->data[i]); 103 psStringAppend (&realName, "/%s", name); 104 psTrace (__func__, 5, "trying %s\n", realName); 105 106 status = stat (realName, &filestat); 107 if (status == 0) { 108 if ((uid == filestat.st_uid) && (filestat.st_mode & S_IRUSR)) 109 goto found; 110 if ((gid == filestat.st_gid) && (filestat.st_mode & S_IRGRP)) 111 goto found; 112 if (filestat.st_mode & S_IROTH) 113 goto found; 114 } 115 psFree (realName); 116 } 117 118 psError(PS_ERR_IO, false, "Cannot find %s configuration file in path\n", description); 119 return false; 120 121 found: 122 *config = psMetadataConfigParse(NULL, &numBadLines, realName, true); 35 123 if (numBadLines > 0) { 36 124 psLogMsg(__func__, PS_LOG_WARN, "%d bad lines in %s configuration file (%s)\n", 37 description, name);125 description, realName); 38 126 } 39 127 if (!*config) { 40 128 psError(PS_ERR_IO, false, "Unable to read %s configuration from %s\n", 41 description, name); 129 description, realName); 130 psFree (realName); 42 131 return false; 43 132 } 44 133 134 psFree (realName); 45 135 return true; 46 136 } … … 57 147 line. 58 148 *****************************************************************************/ 59 bool pmConfigRead( 60 psMetadata **site, 61 psMetadata **camera, 62 psMetadata **recipe, 149 pmConfig *pmConfigRead( 63 150 int *argc, 64 char **argv, 65 const char *recipeName) 66 { 67 /* XXX: We need clarification on what is to be done if these arguments are 68 NULL. 69 PS_ASSERT_PTR_NON_NULL(site, false); 70 PS_ASSERT_PTR_NON_NULL(*site, false); 71 PS_ASSERT_PTR_NON_NULL(camera, false); 72 PS_ASSERT_PTR_NON_NULL(*camera, false); 73 PS_ASSERT_PTR_NON_NULL(recipe, false); 74 PS_ASSERT_PTR_NON_NULL(*recipe, false); 75 PS_ASSERT_INT_POSITIVE(*argc, false); 76 PS_ASSERT_PTR_NON_NULL(argv, false); 77 */ 151 char **argv) 152 { 153 PS_ASSERT_INT_POSITIVE(*argc, false); 154 PS_ASSERT_PTR_NON_NULL(argv, false); 155 156 pmConfig *config = pmConfigAlloc(); // The configuration, containing site, camera and recipes 157 78 158 // 79 159 // The following section of code attempts to determine which file is … … 97 177 "-site command-line switch provided without the required filename --- ignored.\n"); 98 178 } else { 99 siteName = argv[argNum];179 siteName = psStringCopy(argv[argNum]); 100 180 psArgumentRemove(argNum, argc, argv); 101 181 } … … 106 186 if (!siteName) { 107 187 siteName = getenv(PS_SITE); 188 if (siteName) { 189 siteName = psStringCopy (siteName); 190 } 108 191 } 109 192 … … 111 194 // Last chance is ~/.ipprc 112 195 // 113 bool cleanupSiteName = false; // Do I have to psFree siteName?114 196 if (!siteName) { 115 siteName = psStringCopy(PS_DEFAULT_SITE); 116 cleanupSiteName = true; 117 } 118 119 // 120 // We have the connfiguration filename; now we read and parse the config 197 char *home = getenv("HOME"); 198 siteName = psStringCopy(home); 199 psStringAppend(&siteName, "/%s", PS_DEFAULT_SITE); 200 } 201 202 203 // We have the configuration filename; now we read and parse the config 121 204 // file and store in psMetadata struct site. 122 205 // 123 206 124 if (!readConfig(site, siteName, "site")) { 125 return false; 126 } 127 if (cleanupSiteName) { 128 psFree(siteName); 129 } 130 131 132 // 133 // Next, we do a similar thing for the recipe configuration file. The 134 // file is read and parsed into psMetadata struct "recipe". 135 // 136 // 137 argNum = psArgumentGet(*argc, argv, "-recipe"); 138 if (argNum > 0) { 139 psArgumentRemove(argNum, argc, argv); 140 if (argNum >= *argc) { 141 psLogMsg(__func__, PS_LOG_WARN, 142 "-recipe command-line switch provided without the required filename --- ignored.\n"); 143 } else { 144 psArgumentRemove(argNum, argc, argv); 145 readConfig(recipe, argv[argNum], "recipe"); 146 } 147 } 148 // Or, load the recipe from the camera file, if appropriate 149 if (! *recipe && *camera && recipeName) { 150 *recipe = pmConfigRecipeFromCamera(*camera, recipeName); 151 } 152 153 154 // 207 if (!readConfig(&config->site, siteName, "site")) { 208 psFree(config); 209 return NULL; 210 } 211 psFree(siteName); 212 213 // define the config-file search path (configPath) 214 if (configPath) { 215 psFree(configPath); 216 configPath = NULL; 217 } 218 char *path = psMetadataLookupStr(NULL, config->site, "PATH"); 219 if (path) { 220 psList *list = psStringSplit(path, ":"); 221 configPath = psListToArray(list); 222 psFree(list); 223 } 224 155 225 // Next, we do a similar thing for the camera configuration file. The 156 226 // file is read and parsed into psMetadata struct "camera". … … 164 234 } else { 165 235 psArgumentRemove(argNum, argc, argv); 166 readConfig(camera, argv[argNum], "camera"); 167 } 168 } else { 169 // XXX: Not sure is this is correct. 170 *camera = NULL; 236 readConfig(&config->camera, argv[argNum], "camera"); 237 } 238 } 239 240 // Load the recipes from the camera file, if appropriate 241 if (! config->recipes && config->camera) { 242 pmConfigReadRecipes(config); 171 243 } 172 244 … … 183 255 // with a call to psTimeInitialize. 184 256 // 185 psString timeName = psMetadataLookupStr(&mdok, *site, "TIME");257 psString timeName = psMetadataLookupStr(&mdok, config->site, "TIME"); 186 258 if (mdok && timeName) { 187 259 psTrace(__func__, 7, "Initialising psTime with file %s\n", timeName); … … 195 267 // with a call to psLogSetLevel(). 196 268 // 197 int logLevel = psMetadataLookupS32(&mdok, *site, "LOGLEVEL");269 int logLevel = psMetadataLookupS32(&mdok, config->site, "LOGLEVEL"); 198 270 if (mdok && logLevel >= 0) { 199 271 psTrace(__func__, 7, "Setting log level to %d\n", logLevel); … … 206 278 // with a call to psLogSetFormat(). 207 279 // 208 psString logFormat = psMetadataLookupStr(&mdok, *site, "LOGFORMAT");280 psString logFormat = psMetadataLookupStr(&mdok, config->site, "LOGFORMAT"); 209 281 if (mdok && logFormat) { 210 282 psTrace(__func__, 7, "Setting log format to %s\n", logFormat); … … 218 290 // XXX: This is not spec'ed in the SDRS. 219 291 // 220 psString logDest = psMetadataLookupStr(&mdok, *site, "LOGDEST");292 psString logDest = psMetadataLookupStr(&mdok, config->site, "LOGDEST"); 221 293 if (mdok && logDest) { 222 // XXX: Only stdout isprovided for now; this section should be294 // XXX: Only stdout and stderr are provided for now; this section should be 223 295 // expanded in the future to do files, and perhaps even sockets. 224 if (strcasecmp(logDest, "STDOUT") != 0) { 225 psLogMsg(__func__, PS_LOG_WARN, "Only STDOUT is currently supported as a log destination.\n"); 296 int logFD = STDIN_FILENO; // a known invalid value 297 if (!strcasecmp(logDest, "STDOUT")) { 298 logFD = STDOUT_FILENO; 299 } 300 if (!strcasecmp(logDest, "STDERR")) { 301 logFD = STDERR_FILENO; 302 } 303 if (logFD == STDIN_FILENO) { 304 psLogMsg(__func__, PS_LOG_WARN, "Only STDERR and STDOUT currently supported as a log destination.\n"); 305 logFD = STDERR_FILENO; 226 306 } 227 307 psTrace(__func__, 7, "Setting log destination to STDOUT.\n"); 228 // XXX: Use something other than "1" 229 psLogSetDestination(1); 230 } 231 308 psLogSetDestination(logFD); 309 } 232 310 233 311 // … … 236 314 // XXX: This is not spec'ed in the SDRS. 237 315 // 238 psMetadata *trace = psMetadataLookupMD(&mdok, *site, "TRACE");316 psMetadata *trace = psMetadataLookupMD(&mdok, config->site, "TRACE"); 239 317 if (mdok && trace) { 240 318 psMetadataIterator *traceIter = psMetadataIteratorAlloc(trace, PS_LIST_HEAD, NULL); // Iterator … … 264 342 psLogSetLevel(saveLogLevel); 265 343 } 266 return(true); 267 } 268 269 bool pmConfigValidateCamera( 270 const psMetadata *camera, 344 345 return config; 346 } 347 348 349 bool pmConfigValidateCameraFormat( 350 const psMetadata *cameraFormat, 271 351 const psMetadata *header) 272 352 { 273 // Read the rule for that camera 353 // Read the rule for that camera format 274 354 bool mdStatus = true; 275 psMetadata *rule = psMetadataLookupMD(&mdStatus, camera , "RULE");355 psMetadata *rule = psMetadataLookupMD(&mdStatus, cameraFormat, "RULE"); 276 356 if (! mdStatus || ! rule) { 277 357 psLogMsg(__func__, PS_LOG_WARN, "Unable to read rule for camera.\n"); … … 282 362 psMetadataIterator *ruleIter = psMetadataIteratorAlloc(rule, PS_LIST_HEAD, NULL); // Rule iterator 283 363 psMetadataItem *ruleItem = NULL; // Item from the metadata 284 bool match = true; // Does it match? 285 while ((ruleItem = psMetadataGetAndIncrement(ruleIter)) && match) { 364 while ((ruleItem = psMetadataGetAndIncrement(ruleIter))) { 286 365 // Check for the existence of the rule 287 366 psMetadataItem *headerItem = psMetadataLookup((psMetadata*)header, ruleItem->name); 288 367 if (! headerItem || headerItem->type != ruleItem->type) { 289 match = false; 368 psFree(ruleIter); 369 return false; 290 370 } 291 371 … … 297 377 if (strncmp(ruleItem->data.V, headerItem->data.V, 298 378 strlen(ruleItem->data.V)) != 0) { 299 match = false; 379 psFree(ruleIter); 380 return false; 300 381 } 301 382 break; … … 305 386 ruleItem->data.S32, headerItem->data.S32); 306 387 if (ruleItem->data.S32 != headerItem->data.S32) { 307 match = false; 388 psFree(ruleIter); 389 return false; 308 390 } 309 391 break; … … 312 394 ruleItem->data.F32, headerItem->data.F32); 313 395 if (ruleItem->data.F32 != headerItem->data.F32) { 314 match = false; 396 psFree(ruleIter); 397 return false; 315 398 } 316 399 break; … … 319 402 ruleItem->data.F64, headerItem->data.F64); 320 403 if (ruleItem->data.F64 != headerItem->data.F64) { 321 match = false; 404 psFree(ruleIter); 405 return false; 322 406 } 323 407 break; … … 329 413 330 414 psFree(ruleIter); 331 332 return match; 333 } 334 335 336 337 // Work out what camera we have, based on the FITS header and a set of 338 // rules specified in the IPP configuration; return the camera configuration 339 psMetadata *pmConfigCameraFromHeader( 340 const psMetadata *ipprc, // The IPP configuration 341 const psMetadata *header) // The FITS header 342 { 343 bool mdStatus = false; // Metadata lookup status 344 psMetadata *cameras = psMetadataLookupMD(&mdStatus, ipprc, "CAMERAS"); 345 if (! mdStatus) { 346 psError(PS_ERR_IO, false, "Unable to find CAMERAS in the configuration.\n"); 347 return NULL; 348 } 349 psMetadata *winner = NULL; // The camera configuration whose rule first matches 350 // the supplied header 351 // Iterate over the cameras 352 psMetadataIterator *iterator = psMetadataIteratorAlloc(cameras, PS_LIST_HEAD, NULL); 353 psMetadataItem *cameraItem = NULL; // Item from the metadata 354 355 while ((cameraItem = psMetadataGetAndIncrement(iterator))) { 356 // Open the camera information 357 psTrace(__func__, 3, "Inspecting camera %s (%s)\n", cameraItem->name, 358 cameraItem->comment); 359 psMetadata *camera = NULL; // The camera metadata 360 if (cameraItem->type == PS_DATA_METADATA) { 361 camera = psMemIncrRefCounter(cameraItem->data.md); 362 } else if (cameraItem->type == PS_DATA_STRING) { 363 psTrace(__func__, 5, "Reading camera configuration for %s...\n", cameraItem->name); 364 unsigned int badLines = 0; // Number of bad lines in reading camera configuration 365 camera = psMetadataConfigParse(NULL, &badLines, cameraItem->data.V, true); 415 return true; 416 } 417 418 419 static bool formatFromHeader(psMetadata **format, // Format to return 420 psMetadata *camera, // Camera configuration 421 const psMetadata *header, // FITS header 422 const char *cameraName // Name of camera 423 ) 424 { 425 psMetadata *testFormat; 426 427 assert(format); 428 assert(camera); 429 assert(header); 430 431 bool result = false; // Did we find the first match? 432 433 // Read the list of formats 434 bool mdok = true; // Status of MD lookup 435 psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS"); // List of formats 436 if (!mdok || !formats) { 437 psLogMsg(__func__, PS_LOG_WARN, "Unable to find list of FORMATS in camera %s --- ignored\n", 438 cameraName); 439 return false; 440 } 441 // Iterate over the formats 442 psMetadataIterator *formatsIter = psMetadataIteratorAlloc(formats, PS_LIST_HEAD, NULL); 443 psMetadataItem *formatsItem = NULL; // Item from formats 444 while ((formatsItem = psMetadataGetAndIncrement(formatsIter))) { 445 if (formatsItem->type != PS_DATA_STRING) { 446 psLogMsg(__func__, PS_LOG_WARN, "In camera %s, camera format %s is not of type STR --- " 447 "ignored.\n", cameraName, formatsItem->name); 448 continue; 449 } 450 psTrace(__func__, 5, "Reading camera format for %s...\n", formatsItem->name); 451 // unsigned int badLines = 0; // Number of bad lines in reading camera configuration 452 // Format to test against what we've got 453 454 if (!readConfig(&testFormat, formatsItem->data.V, formatsItem->name)) { 455 psLogMsg(__func__, PS_LOG_WARN, "trouble reading reading camera format %s\n", formatsItem->name); 456 psFree(testFormat); 457 continue; 458 } 459 460 # if (0) 461 psMetadata *testFormat = psMetadataConfigParse(NULL, &badLines, formatsItem->data.V, true); 462 if (badLines > 0) { 463 psLogMsg(__func__, PS_LOG_WARN, "%d bad lines encountered while reading camera" 464 "format %s\n", badLines, formatsItem->name); 465 } 466 # endif 467 468 if (pmConfigValidateCameraFormat(testFormat, header)) { 469 if (!*format) { 470 psLogMsg(__func__, PS_LOG_INFO, "Camera %s, format %s matches header.\n", cameraName, 471 formatsItem->name); 472 *format = psMemIncrRefCounter(testFormat); 473 result = true; 474 } else { 475 psLogMsg(__func__, PS_LOG_WARN, "Camera %s, format %s also matches header --- ignored.\n", 476 cameraName, formatsItem->name); 477 } 478 } 479 psFree(testFormat); 480 } 481 psFree(formatsIter); 482 483 return result; 484 } 485 486 // Work out what camera we have, based on the FITS header and a set of rules specified in the IPP 487 // configuration; return the camera configuration and format 488 psMetadata *pmConfigCameraFormatFromHeader( 489 pmConfig *config, // The configuration 490 const psMetadata *header // The FITS header 491 ) 492 { 493 psMetadata *format = NULL; // The winning format 494 bool mdok = false; // Metadata lookup status 495 psMetadata *testCamera = NULL; 496 497 // If we don't know what sort of camera we have, we try all that we know 498 if (! config->camera) { 499 psMetadata *cameras = psMetadataLookupMD(&mdok, config->site, "CAMERAS"); 500 if (! mdok) { 501 psError(PS_ERR_IO, false, "Unable to find CAMERAS in the configuration.\n"); 502 return false; 503 } 504 505 // Iterate over the cameras 506 psMetadataIterator *camerasIter = psMetadataIteratorAlloc(cameras, PS_LIST_HEAD, NULL); 507 psMetadataItem *camerasItem = NULL; // Item from the metadata 508 while ((camerasItem = psMetadataGetAndIncrement(camerasIter))) { 509 // Open the camera information 510 psTrace(__func__, 3, "Inspecting camera %s (%s)\n", camerasItem->name, camerasItem->comment); 511 if (camerasItem->type != PS_DATA_STRING) { 512 psLogMsg(__func__, PS_LOG_WARN, "Camera configuration for %s in CAMERAS is not of type STR " 513 "--- ignored.\n", camerasItem->name); 514 continue; 515 } 516 517 psTrace(__func__, 5, "Reading camera configuration for %s...\n", camerasItem->name); 518 // unsigned int badLines = 0; // Number of bad lines in reading camera configuration 519 // Camera to test against what we've got: 520 521 if (!readConfig(&testCamera, camerasItem->data.V, camerasItem->name)) { 522 psLogMsg(__func__, PS_LOG_WARN, "trouble reading reading camera configuration %s\n", camerasItem->name); 523 psFree(testCamera); 524 continue; 525 } 526 527 # if (0) 528 psMetadata *testCamera = psMetadataConfigParse(NULL, &badLines, camerasItem->data.V, true); 366 529 if (badLines > 0) { 367 530 psLogMsg(__func__, PS_LOG_WARN, "%d bad lines encountered while reading camera" 368 "configuration %s\n", badLines, cameraItem->name); 369 } 370 } 371 372 if (! camera) { 373 psLogMsg(__func__, PS_LOG_WARN, "Unable to interpret camera configuration for %s (%s)\n", 374 cameraItem->name, cameraItem->comment); 375 continue; 376 } 377 378 if (pmConfigValidateCamera(camera, header)) { 379 if (! winner) { 380 // This is the first match 381 winner = psMemIncrRefCounter(camera); 382 psLogMsg(__func__, PS_LOG_INFO, "FITS header matches camera %s\n", 383 cameraItem->name); 384 } else { 385 // We have a duplicate match 386 psLogMsg(__func__, PS_LOG_WARN, "Additional camera found that matches the rules: %s\n", 387 cameraItem->name); 388 } 389 } // Done inspecting the camera 390 391 psFree(camera); 392 393 } // Done looking at all cameras 394 if (! winner) { 395 psError(PS_ERR_IO, true, "Unable to find an camera that matches input FITS header!\n"); 396 } 397 398 psFree(iterator); 399 return winner; 400 } 401 402 psMetadata *pmConfigRecipeFromCamera( 403 const psMetadata *camera, 404 const char *recipeName) 405 { 406 PS_ASSERT_PTR_NON_NULL(camera, false); 407 PS_ASSERT_PTR_NON_NULL(recipeName, false); 408 409 psMetadata *recipe = NULL; // Recipe to read 531 "configuration %s\n", badLines, camerasItem->name); 532 } 533 # endif 534 535 if (! testCamera) { 536 psLogMsg(__func__, PS_LOG_WARN, "Unable to interpret camera configuration for %s (%s) --- " 537 "ignored\n", camerasItem->name, camerasItem->comment); 538 continue; 539 } 540 541 if (formatFromHeader(&format, testCamera, header, camerasItem->name)) { 542 config->camera = psMemIncrRefCounter(testCamera); 543 } 544 psFree(testCamera); 545 } // Done looking at all cameras 546 psFree(camerasIter); 547 548 if (! config->camera) { 549 psError(PS_ERR_IO, true, "Unable to find a camera that matches input FITS header!\n"); 550 return NULL; 551 } 552 553 // Now we have the camera, we can read the recipes 554 if (!config->recipes) { 555 pmConfigReadRecipes(config); 556 } 557 558 return format; 559 } 560 561 // Otherwise, try the specific camera 562 if (! formatFromHeader(&format, config->camera, header, "specified camera")) { 563 psError(PS_ERR_IO, true, "Unable to find a format with the specified camera that matches the " 564 "given header.\n"); 565 return NULL; 566 } 567 return format; 568 } 569 570 bool pmConfigReadRecipes( 571 pmConfig *config 572 ) 573 { 574 PS_ASSERT_PTR_NON_NULL(config, false); 575 PS_ASSERT_PTR_NON_NULL(config->camera, false); 576 577 if (!config->recipes) { 578 config->recipes = psMetadataAlloc(); 579 } 580 410 581 bool mdok = true; // Status of MD lookup 411 psMetadata *recipes = psMetadataLookupMD(&mdok, c amera, "RECIPES"); // The list of recipes582 psMetadata *recipes = psMetadataLookupMD(&mdok, config->camera, "RECIPES"); // The list of recipes 412 583 if (! mdok || ! recipes) { 413 584 psLogMsg(__func__, PS_LOG_WARN, "RECIPES in the camera configuration file is not of type METADATA\n"); 414 } else { 415 psString recipeFileName = psMetadataLookupStr(&mdok, recipes, recipeName); 416 (void)readConfig(&recipe, recipeFileName, "recipe"); 417 } 418 419 return recipe; 585 return false; 586 } 587 // Go through the recipes and load each one 588 psMetadataIterator *recipesIter = psMetadataIteratorAlloc(recipes, PS_LIST_HEAD, NULL); // Iterator 589 psMetadataItem *recipesItem = NULL; // Item from iteration 590 while ((recipesItem = psMetadataGetAndIncrement(recipesIter))) { 591 if (recipesItem->type != PS_DATA_STRING) { 592 psLogMsg(__func__, PS_LOG_WARN, "Filename for recipe %s isn't of type STR --- ignored.\n", 593 recipesItem->name); 594 continue; 595 } 596 597 psMetadata *recipe = NULL; // Recipe from file 598 if (readConfig(&recipe, recipesItem->data.V, "recipe")) { 599 psMetadataAdd(config->recipes, PS_LIST_TAIL, recipesItem->name, 600 PS_DATA_METADATA | PS_META_REPLACE, recipesItem->comment, recipe); 601 } 602 psFree(recipe); // Drop reference 603 } 604 psFree(recipesIter); 605 606 return true; 420 607 } 421 608 … … 423 610 pmConfigDB(*site) 424 611 612 XXX: this should allow the option of having NO database server, if chosen by config 425 613 XXX: What should we use for the Database namespace in the call to psDBInit()? 426 614 This is currently NULL. 427 615 *****************************************************************************/ 428 616 429 #ifdef OMIT_PSDB430 psDB *pmConfigDB(psMetadata *site)431 {432 psError(PS_ERR_UNKNOWN, true, "pslib was built without psDB support");433 return NULL;434 }435 #else436 617 psDB *pmConfigDB( 437 618 psMetadata *site) … … 442 623 psBool mdStatus03 = false; 443 624 625 // XXX leaky strings 444 626 psString dbServer = psMetadataLookupStr(&mdStatus01, site, "DBSERVER"); 445 627 psString dbUsername = psMetadataLookupStr(&mdStatus02, site, "DBUSER"); 446 628 psString dbPassword = psMetadataLookupStr(&mdStatus03, site, "DBPASSWORD"); 447 629 psString dbName = psMetadataLookupStr(&mdStatus01, site, "DBNAME"); 448 449 630 if (!(mdStatus01 & mdStatus02 & mdStatus03)) { 450 631 psLogMsg(__func__, PS_LOG_WARN, "Could not determine database server name, userID, and password from site metadata.\n"); 451 return NULL; 452 } 453 454 455 456 psDB *dbh = psDBInit(dbServer, dbUsername, dbPassword, dbName); 457 psFree(dbServer); 458 psFree(dbUsername); 459 psFree(dbPassword); 460 psFree(dbName); 461 462 if (!dbh) { 463 psError(PS_ERR_UNKNOWN, false, "database connection failed"); 464 } 465 466 return dbh; 467 } 468 #endif 632 return(NULL); 633 } 634 635 return(psDBInit(dbServer, dbUsername, dbPassword, dbName)); 636 } 637 638 639 bool pmConfigConformHeader(psMetadata *header, // Header to conform 640 const psMetadata *format // Camera format 641 ) 642 { 643 bool mdok = true; // Status of MD lookup 644 psMetadata *rules = psMetadataLookupMD(&mdok, format, "RULE"); // How to identify this format 645 if (!mdok || !rules) { 646 psError(PS_ERR_IO, false, "Unable to find RULE in camer format.\n"); 647 return false; 648 } 649 650 psMetadataIterator *rulesIter = psMetadataIteratorAlloc(rules, PS_LIST_HEAD, NULL); // Iterator for rules 651 psMetadataItem *rulesItem = NULL; // Item from iteration 652 while ((rulesItem = psMetadataGetAndIncrement(rulesIter))) { 653 psMetadataItem *newItem = psMetadataItemCopy(rulesItem); // Copy of item 654 psMetadataAddItem(header, newItem, PS_LIST_TAIL, PS_META_REPLACE); 655 psFree(newItem); // Drop reference 656 } 657 psFree(rulesIter); 658 659 return true; 660 } 661 662 // given the 'file' and 'list' words, find the arguments associated with these words 663 // and interpret them as lists of files. return an array of the resulting filenames. 664 psArray *pmConfigFileSets (int *argc, char **argv, char *file, char *list) 665 { 666 667 int Narg; 668 669 // we load all input files onto a psArray, to be parsed later 670 psArray *input = psArrayAlloc (16); 671 input->n = 0; 672 673 // load the list of filenames the supplied file (may be a glob: "file*.fits") 674 if ((Narg = psArgumentGet (*argc, argv, file))) { 675 glob_t globList; 676 psArgumentRemove (Narg, argc, argv); 677 globList.gl_offs = 0; 678 glob (argv[Narg], 0, NULL, &globList); 679 for (int i = 0; i < globList.gl_pathc; i++) { 680 char *filename = psStringCopy (globList.gl_pathv[i]); 681 psArrayAdd (input, 16, filename); 682 psFree (filename); 683 } 684 psArgumentRemove (Narg, argc, argv); 685 } 686 687 // load the list from the supplied text file 688 if ((Narg = psArgumentGet (*argc, argv, list))) { 689 int nItems; 690 char line[1024]; // XXX limits the list lines to 1024 chars 691 char word[1024]; 692 char *filename; 693 694 psArgumentRemove (Narg, argc, argv); 695 FILE *f = fopen (argv[Narg], "r"); 696 if (f == NULL) { 697 psAbort ("psphot", "unable to open specified list file"); 698 } 699 while (fgets (line, 1024, f) != NULL) { 700 nItems = sscanf (line, "%s", word); 701 switch (nItems) { 702 case 0: 703 break; 704 case 1: 705 filename = psStringCopy (word); 706 psArrayAdd (input, 16, filename); 707 psFree (filename); 708 break; 709 default: 710 // rigid format, no comments allowed? 711 psAbort ("pmConfig", "error parsing input list file"); 712 break; 713 } 714 } 715 psArgumentRemove (Narg, argc, argv); 716 } 717 718 return input; 719 } 720 721 bool pmConfigFileSetsMD (psMetadata *metadata, int *argc, char **argv, char *name, char *file, char *list) 722 { 723 724 psArray *files = pmConfigFileSets (argc, argv, file, list); 725 if (files->n == 0) { 726 psFree (files); 727 return false; 728 } 729 730 psMetadataAddPtr (metadata, PS_LIST_TAIL, name, PS_DATA_ARRAY, "", files); 731 psFree (files); 732 return true; 733 } -
trunk/psModules/src/config/pmConfig.h
r6872 r6873 3 3 * @author PAP, IfA 4 4 * 5 * @version $Revision: 1. 4$ $Name: not supported by cvs2svn $6 * @date $Date: 2006-04-17 18: 01:05$5 * @version $Revision: 1.5 $ $Name: not supported by cvs2svn $ 6 * @date $Date: 2006-04-17 18:10:08 $ 7 7 * 8 8 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii -
trunk/psModules/src/detrend/pmFlatField.c
r6872 r6873 24 24 * @author Ross Harman, MHPCC 25 25 * 26 * @version $Revision: 1. 5$ $Name: not supported by cvs2svn $27 * @date $Date: 2006-04-17 18: 01:05$26 * @version $Revision: 1.6 $ $Name: not supported by cvs2svn $ 27 * @date $Date: 2006-04-17 18:10:08 $ 28 28 * 29 29 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii -
trunk/psModules/src/detrend/pmMaskBadPixels.c
r6872 r6873 24 24 * @author Ross Harman, MHPCC 25 25 * 26 * @version $Revision: 1. 4$ $Name: not supported by cvs2svn $27 * @date $Date: 2006-04-17 18: 01:05$26 * @version $Revision: 1.5 $ $Name: not supported by cvs2svn $ 27 * @date $Date: 2006-04-17 18:10:08 $ 28 28 * 29 29 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii -
trunk/psModules/src/detrend/pmMaskBadPixelsErrors.h
r6872 r6873 12 12 * @author Ross Harman, MHPCC 13 13 * 14 * @version $Revision: 1. 2$ $Name: not supported by cvs2svn $15 * @date $Date: 2006-04-17 18: 01:05$14 * @version $Revision: 1.3 $ $Name: not supported by cvs2svn $ 15 * @date $Date: 2006-04-17 18:10:08 $ 16 16 * 17 17 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii -
trunk/psModules/src/detrend/pmNonLinear.c
r6872 r6873 10 10 * @author GLG, MHPCC 11 11 * 12 * @version $Revision: 1. 6$ $Name: not supported by cvs2svn $13 * @date $Date: 2006-04-17 18: 01:05$12 * @version $Revision: 1.7 $ $Name: not supported by cvs2svn $ 13 * @date $Date: 2006-04-17 18:10:08 $ 14 14 * 15 15 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii -
trunk/psModules/src/imcombine/pmReadoutCombine.c
r6511 r6873 5 5 * @author GLG, MHPCC 6 6 * 7 * @version $Revision: 1. 6$ $Name: not supported by cvs2svn $8 * @date $Date: 2006-0 3-04 01:01:33$7 * @version $Revision: 1.7 $ $Name: not supported by cvs2svn $ 8 * @date $Date: 2006-04-17 18:10:08 $ 9 9 * 10 10 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii … … 36 36 } 37 37 38 static void pmCombineParamsFree (pmCombineParams *params) 39 { 40 41 if (params == NULL) 42 return; 43 44 psFree (params->stats); 45 return; 46 } 47 48 pmCombineParams *pmCombineParamsAlloc (psStatsOptions statsOptions) 49 { 50 51 pmCombineParams *params = psAlloc (sizeof(pmCombineParams)); 52 psMemSetDeallocator(params, (psFreeFunc) pmCombineParamsFree); 53 54 params->stats = psStatsAlloc (statsOptions); 55 params->maskVal = 0; 56 params->fracHigh = 0.25; 57 params->fracHigh = 0.25; 58 params->nKeep = 3; 59 60 return (params); 61 } 62 38 63 /****************************************************************************** 39 64 XXX: Must add support for S16 and S32 types. F32 currently supported. 40 65 *****************************************************************************/ 41 66 psImage *pmReadoutCombine(psImage *output, 42 const psList *inputs, 43 psCombineParams *params, 67 const psArray *inputs, 44 68 const psVector *zero, 45 69 const psVector *scale, 70 pmCombineParams *params, 46 71 bool applyZeroScale, 47 72 psF32 gain, 48 73 psF32 readnoise) 74 { 75 PS_ASSERT_PTR_NON_NULL(inputs, NULL); 76 PS_ASSERT_PTR_NON_NULL(params, NULL); 77 PS_ASSERT_PTR_NON_NULL(params->stats, NULL); 78 if (zero != NULL) { 79 PS_ASSERT_VECTOR_TYPE(zero, PS_TYPE_F32, NULL); 80 // PS_ASSERT_VECTOR_TYPE_S16_S32_F32(zero, NULL); 81 } 82 if (scale != NULL) { 83 PS_ASSERT_VECTOR_TYPE(scale, PS_TYPE_F32, NULL); 84 // PS_ASSERT_VECTOR_TYPE_S16_S32_F32(scale, NULL); 85 } 86 if ((zero != NULL) && (scale != NULL)) { 87 PS_ASSERT_VECTOR_TYPE_EQUAL(zero, scale, NULL); 88 // PS_ASSERT_VECTOR_TYPE_S16_S32_F32(scale, NULL); 89 } 90 91 psStats *stats = params->stats; 92 psS32 maxInputCols = 0; 93 psS32 maxInputRows = 0; 94 psS32 minInputCols = PS_MAX_S32; 95 psS32 minInputRows = PS_MAX_S32; 96 pmReadout *tmpReadout = NULL; 97 psS32 tmpI; 98 psElemType outputType = PS_TYPE_F32; 99 100 if (DetermineNumBits(stats->options) != 1) { 101 psError(PS_ERR_UNKNOWN, true, 102 "Multiple statistical options have been requested. Returning NULL.\n"); 103 return(NULL); 104 } 105 106 // We step through each readout in the input image list. If any readout is 107 // NULL, empty, or has the wrong type, we generate an error and return 108 // NULL. We determine how big of an output image is needed to combine 109 // these input images. We do this by taking the 110 // max(readout->col0 + readout->numCols + image->col0 + image->numCols) 111 // max(readout->row0 + readout->numRows + image->row0 + image->numRows) 112 // 113 for (int i = 0; i < inputs->n; i++) { 114 tmpReadout = inputs->data[i]; 115 PS_ASSERT_READOUT_NON_NULL(tmpReadout, output); 116 PS_ASSERT_READOUT_NON_EMPTY(tmpReadout, output); 117 PS_ASSERT_READOUT_TYPE(tmpReadout, PS_TYPE_F32, output); 118 119 minInputRows = PS_MIN(minInputRows, (tmpReadout->row0 + tmpReadout->image->row0)); 120 tmpI = tmpReadout->row0 + 121 tmpReadout->image->row0 + 122 tmpReadout->image->numRows; 123 maxInputRows = PS_MAX(maxInputRows, tmpI); 124 125 minInputCols = PS_MIN(minInputCols, (tmpReadout->col0 + tmpReadout->image->col0)); 126 tmpI = tmpReadout->col0 + 127 tmpReadout->image->col0 + 128 tmpReadout->image->numCols; 129 maxInputCols = PS_MAX(maxInputCols, tmpI); 130 } 131 132 // We ensure that the zero vector is of the proper size. 133 if (zero != NULL) { 134 PS_ASSERT_VECTOR_TYPE(zero, PS_TYPE_F32, NULL); 135 if (zero->n < inputs->n) { 136 psError(PS_ERR_UNKNOWN, true, "zero vector has incorrect size (%d). Returning NULL.\n", zero->n); 137 return(NULL); 138 } else if (zero->n > inputs->n) { 139 // XXX EAM : abort on this condition? is probably an error 140 psLogMsg(__func__, PS_LOG_WARN, 141 "WARNING: the zero vector too many elements (%d)\n", zero->n); 142 } 143 } 144 145 // We ensure that the scale vector is of the proper size. 146 if (scale != NULL) { 147 PS_ASSERT_VECTOR_TYPE(scale, PS_TYPE_F32, NULL); 148 if (scale->n < inputs->n) { 149 psError(PS_ERR_UNKNOWN, true, "scale vector has incorrect size (%d). Returning NULL.\n", scale->n); 150 return(NULL); 151 } else if (scale->n > inputs->n) { 152 // XXX EAM : abort on this condition? is probably an error 153 psLogMsg(__func__, PS_LOG_WARN, 154 "WARNING: the scale vector has too many elements (%d)\n", scale->n); 155 } 156 } 157 158 // At this point, the following variables have been computed: 159 // maxInputRows: the largest input row value, in output image space. 160 // maxInputCols: the largest input column value, in output image space. 161 // minInputRows: the smallest input row value, in output image space. 162 // minInputCols: the smallest input column value, in output image space. 163 // 164 if (output == NULL) { 165 output = psImageAlloc(maxInputCols-minInputCols, maxInputRows-minInputRows, outputType); 166 *(psS32 *) &(output->col0) = minInputCols; 167 *(psS32 *) &(output->row0) = minInputRows; 168 } else { 169 170 // XXX EAM : recycle the existing output image? why would we care about the existing pixels? 171 PS_ASSERT_IMAGE_TYPE(output, PS_TYPE_F32, NULL); 172 if (((output->col0 + output->numCols) < maxInputCols) || 173 ((output->row0 + output->numRows) < maxInputRows)) { 174 psError(PS_ERR_UNKNOWN, true, 175 "Output image (%d, %d) is too small to hold combined images. Returning NULL.\n", 176 output->row0 + output->numRows, 177 output->col0 + output->numCols); 178 return(NULL); 179 } 180 181 // reset output origin using logic of above 182 *(psS32 *) &(output->col0) = minInputCols; 183 *(psS32 *) &(output->row0) = minInputRows; 184 } 185 186 psVector *tmpPixels = psVectorAlloc(inputs->n, PS_TYPE_F32); 187 psVector *tmpPixelsKeep = psVectorAlloc(inputs->n, PS_TYPE_F32); 188 psVector *outRowLower = psVectorAlloc(inputs->n, PS_TYPE_U32); 189 psVector *outRowUpper = psVectorAlloc(inputs->n, PS_TYPE_U32); 190 psVector *outColLower = psVectorAlloc(inputs->n, PS_TYPE_U32); 191 psVector *outColUpper = psVectorAlloc(inputs->n, PS_TYPE_U32); 192 193 // For each input readout, we store the min/max pixel indices for that readout, in detector coordinates, 194 // in the psVectors (outRowLower, outColLower, outRowUpper, outColUpper). 195 for (int i = 0; i < inputs->n; i++) { 196 tmpReadout = (pmReadout *) inputs->data[i]; 197 outRowLower->data.U32[i] = tmpReadout->row0 + tmpReadout->image->row0; 198 outColLower->data.U32[i] = tmpReadout->col0 + tmpReadout->image->col0; 199 outRowUpper->data.U32[i] = tmpReadout->row0 + 200 tmpReadout->image->row0 + 201 tmpReadout->image->numRows; 202 outColUpper->data.U32[i] = tmpReadout->col0 + 203 tmpReadout->image->col0 + 204 tmpReadout->image->numCols; 205 } 206 207 // We loop through each pixel in the output image. We loop through each 208 // input readout. We determine if that output pixel is contained in the 209 // image from that readout. If so, we save it in psVector tmpPixels. 210 // If not, we set a mask for that element in tmpPixels. Then, we mask off 211 // pixels not between fracLow and fracHigh. Then we call the vector 212 // stats routine on those pixels/mask. Then we set the output pixel value 213 // to the result of the stats call. 214 215 int nx, ny; 216 int nKeep, nMin; 217 float keepFrac = 1.0 - params->fracLow - params->fracHigh; 218 float value = 0; 219 psF32 *saveVector = tmpPixelsKeep->data.F32; 220 221 for (int j = output->row0; j < (output->row0 + output->numRows) ; j++) { 222 if (j % 10 == 0) 223 fprintf (stderr, "."); 224 for (int i = output->col0; i < (output->col0 + output->numCols) ; i++) { 225 int nPix = 0; 226 for (int r = 0; r < inputs->n; r++) { 227 tmpReadout = (pmReadout *) inputs->data[r]; 228 229 // psTrace (__func__, 6, "[%d][%d]: [%d][%d] to [%d][%d]\n", i, j, outColLower->data.U32[r], outRowLower->data.U32[r], outColUpper->data.U32[r], outRowUpper->data.U32[r]); 230 if (i < outColLower->data.U32[r]) 231 continue; 232 if (i >= outColUpper->data.U32[r]) 233 continue; 234 if (j < outRowLower->data.U32[r]) 235 continue; 236 if (j >= outRowUpper->data.U32[r]) 237 continue; 238 239 nx = i - (tmpReadout->col0 + tmpReadout->image->col0); 240 ny = j - (tmpReadout->row0 + tmpReadout->image->row0); 241 242 if (tmpReadout->mask != NULL) { 243 if (tmpReadout->mask->data.U8[ny][nx] && params->maskVal) 244 continue; 245 } 246 247 tmpPixels->data.F32[nPix] = tmpReadout->image->data.F32[ny][nx]; 248 // psTrace (__func__, 6, "readout[%d], image [%d][%d] is %f\n", r, i, j, tmpPixels->data.F32[nPix]); 249 nPix ++; 250 } 251 tmpPixels->n = nPix; 252 253 // are there enough valid pixels to apply fracLow,fracHigh? 254 nKeep = nPix * keepFrac; 255 if (nKeep >= params->nKeep) { 256 psVectorSort (tmpPixels, tmpPixels); 257 nMin = nPix * params->fracLow; 258 tmpPixelsKeep->data.F32 = &tmpPixels->data.F32[nMin]; 259 tmpPixelsKeep->n = nKeep; 260 } else { 261 tmpPixelsKeep->data.F32 = tmpPixels->data.F32; 262 tmpPixelsKeep->n = nPix; 263 } 264 265 // tmpPixelsKeep is already sorted. sample mean and median are very easy 266 if (stats->options & PS_STAT_SAMPLE_MEAN) { 267 value = 0; 268 for (int r = 0; r < tmpPixelsKeep->n; r++) { 269 value += tmpPixelsKeep->data.F32[r]; 270 } 271 if (tmpPixelsKeep->n == 0) { 272 value = 0; 273 } else { 274 value = value / tmpPixelsKeep->n; 275 } 276 } 277 if (stats->options & PS_STAT_SAMPLE_MEDIAN) { 278 int r = tmpPixelsKeep->n / 2; 279 if (tmpPixelsKeep->n == 0) { 280 value = 0; 281 goto got_value; 282 } 283 if (tmpPixelsKeep->n % 2 == 1) { 284 int r = 0.5*tmpPixelsKeep->n; 285 value = tmpPixelsKeep->data.F32[r]; 286 goto got_value; 287 } 288 if (tmpPixelsKeep->n % 2 == 0) { 289 value = 0.5*(tmpPixelsKeep->data.F32[r] + 290 tmpPixelsKeep->data.F32[r-1]); 291 goto got_value; 292 } 293 } 294 got_value: 295 output->data.F32[j-output->row0][i-output->col0] = value; 296 } 297 } 298 tmpPixelsKeep->data.F32 = saveVector; 299 300 psFree(tmpPixels); 301 psFree(tmpPixelsKeep); 302 psFree(outRowLower); 303 psFree(outRowUpper); 304 psFree(outColLower); 305 psFree(outColUpper); 306 307 return(output); 308 } 309 310 /****************************************************************************** 311 XXX: Must add support for S16 and S32 types. F32 currently supported. 312 *****************************************************************************/ 313 psImage *pmReadoutCombine_OLD(psImage *output, 314 const psList *inputs, 315 pmCombineParams *params, 316 const psVector *zero, 317 const psVector *scale, 318 bool applyZeroScale, 319 psF32 gain, 320 psF32 readnoise) 49 321 { 50 322 PS_ASSERT_PTR_NON_NULL(inputs, NULL); … … 418 690 psRegion minRegion; 419 691 psRegion maxRegion; 420 psStats *minStats = psStatsAlloc(PS_STAT_ FITTED_MEAN);421 psStats *maxStats = psStatsAlloc(PS_STAT_ FITTED_MEAN);692 psStats *minStats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); 693 psStats *maxStats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); 422 694 psStats *diffStats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); 423 695 psVector *diffs = psVectorAlloc(fringePoints->n, PS_TYPE_F32); … … 454 726 } 455 727 456 fp->midValue = 0.5 * (maxStats-> fittedMean + minStats->fittedMean);457 fp->delta = maxStats-> fittedMean - minStats->fittedMean;728 fp->midValue = 0.5 * (maxStats->robustMedian + minStats->robustMedian); 729 fp->delta = maxStats->robustMedian - minStats->robustMedian; 458 730 diffs->data.F32[i] = fp->delta; 459 731 } … … 464 736 psFree(diffs); 465 737 if (diffStats == NULL) { 466 psError(PS_ERR_UNKNOWN, true, "Could not determine fittedmedian of the differences.\n");738 psError(PS_ERR_UNKNOWN, true, "Could not determine robust median of the differences.\n"); 467 739 return(NULL); 468 740 } 469 741 return(diffStats); 470 742 } 471 472 473 743 474 744 /** -
trunk/psModules/src/imcombine/pmReadoutCombine.h
r6872 r6873 5 5 * @author GLG, MHPCC 6 6 * 7 * @version $Revision: 1. 4$ $Name: not supported by cvs2svn $8 * @date $Date: 2006-04-17 18: 01:05$7 * @version $Revision: 1.5 $ $Name: not supported by cvs2svn $ 8 * @date $Date: 2006-04-17 18:10:08 $ 9 9 * 10 10 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii -
trunk/psModules/src/imsubtract/pmSubtractBias.c
r6511 r6873 1 ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 // XXX WARNING: I have completely replaced this file with an OLD VERSION (that works) instead of the 3 // one that was being worked on. 4 ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 5 1 6 /** @file pmSubtractBias.c 2 7 * … … 6 11 * @author GLG, MHPCC 7 12 * 8 * @version $Revision: 1.1 1$ $Name: not supported by cvs2svn $9 * @date $Date: 2006-0 3-04 01:01:33$13 * @version $Revision: 1.12 $ $Name: not supported by cvs2svn $ 14 * @date $Date: 2006-04-17 18:10:08 $ 10 15 * 11 16 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii 12 17 * 13 18 */ 14 /*****************************************************************************/ 15 /* INCLUDE FILES */ 16 /*****************************************************************************/ 17 #include <stdio.h> 18 #include <math.h> 19 #include <string.h> 20 #include "pslib.h" 19 21 20 #if HAVE_CONFIG_H 22 21 #include <config.h> 23 22 #endif 23 24 #include <assert.h> 24 25 #include "pmSubtractBias.h" 25 26 26 /*****************************************************************************/ 27 /* DEFINE STATEMENTS */ 28 /*****************************************************************************/ 27 #define PM_SUBTRACT_BIAS_POLYNOMIAL_ORDER 2 28 #define PM_SUBTRACT_BIAS_SPLINE_ORDER 3 29 30 31 #define MAX(a,b) ((a) > (b) ? (a) : (b)) 32 #define MIN(a,b) ((a) < (b) ? (a) : (b)) 33 34 29 35 // XXX: put these in psConstants.h 30 void PS_POLY1D_PRINT( 31 psPolynomial1D *poly) 36 void PS_POLY1D_PRINT(psPolynomial1D *poly) 32 37 { 33 38 printf("-------------- PS_POLY1D_PRINT() --------------\n"); … … 57 62 }\ 58 63 59 /*****************************************************************************/ 60 /* TYPE DEFINITIONS */ 61 /*****************************************************************************/ 62 63 /*****************************************************************************/ 64 /* GLOBAL VARIABLES */ 65 /*****************************************************************************/ 66 psS32 currentId = 0; // XXX: remove 67 psS32 memLeaks = 0; // XXX: remove 68 //PRINT_MEMLEAKS(8); XXX 69 /*****************************************************************************/ 70 /* FILE STATIC VARIABLES */ 71 /*****************************************************************************/ 72 73 /*****************************************************************************/ 74 /* FUNCTION IMPLEMENTATION - LOCAL */ 75 /*****************************************************************************/ 64 65 void overscanOptionsFree(pmOverscanOptions *options) 66 { 67 psFree(options->stat); 68 psFree(options->poly); 69 psFree(options->spline); 70 } 71 72 pmOverscanOptions *pmOverscanOptionsAlloc(bool single, pmFit fitType, unsigned int order, psStats *stat) 73 { 74 pmOverscanOptions *opts = psAlloc(sizeof(pmOverscanOptions)); 75 psMemSetDeallocator(opts, (psFreeFunc)overscanOptionsFree); 76 77 // Inputs 78 opts->single = single; 79 opts->fitType = fitType; 80 opts->order = order; 81 opts->stat = psMemIncrRefCounter(stat); 82 83 // Outputs 84 opts->poly = NULL; 85 opts->spline = NULL; 86 87 return opts; 88 } 89 76 90 77 91 /****************************************************************************** 78 psSubtractFrame(): this routine will take as input the pmReadout for the input 79 image and a pmReadout for the bias image. The bias image is subtracted in 80 place from the input image. We assume that sizes and types are checked 81 elsewhere. 82 83 XXX: Verify that the image and readout offsets are being used the right way. 84 85 XXX: Ensure that it does the correct thing with image size. 92 psSubtractFrame(): this routine will take as input a readout for the input 93 image and a readout for the bias image. The bias image is subtracted in 94 place from the input image. 86 95 *****************************************************************************/ 87 static pmReadout *SubtractFrame( 88 pmReadout *in, 89 const pmReadout *bias) 90 { 91 // XXX: When did the ->row0 and ->col0 offsets get coded? 92 for (psS32 i=0;i<in->image->numRows;i++) { 93 for (psS32 j=0;j<in->image->numCols;j++) { 94 in->image->data.F32[i][j]-= 95 bias->image->data.F32[i+in->row0-bias->row0][j+in->col0-bias->col0]; 96 97 if ((in->mask != NULL) && (bias->mask != NULL)) { 98 (in->mask->data.U8[i][j])|= 99 bias->mask->data.U8[i+in->row0-bias->row0][j+in->col0-bias->col0]; 96 static bool SubtractFrame(pmReadout *in,// Input readout 97 const pmReadout *sub, // Readout to be subtracted from input 98 float scale // Scale to apply before subtracting 99 ) 100 { 101 assert(in); 102 assert(sub); 103 104 psImage *inImage = in->image; // The input image 105 psImage *inMask = in->mask; // The input mask 106 psImage *subImage = sub->image; // The image to be subtracted 107 psImage *subMask = sub->mask; // The mask for the subtraction image 108 109 // Offsets of the cells 110 int x0in = psMetadataLookupS32(NULL, in->parent->concepts, "CELL.X0"); 111 int y0in = psMetadataLookupS32(NULL, in->parent->concepts, "CELL.Y0"); 112 int x0sub = psMetadataLookupS32(NULL, sub->parent->concepts, "CELL.X0"); 113 int y0sub = psMetadataLookupS32(NULL, sub->parent->concepts, "CELL.Y0"); 114 115 if ((inImage->numCols + x0in - x0sub) > subImage->numCols) { 116 psError(PS_ERR_UNKNOWN, true, "Image does not have enough columns for subtraction.\n"); 117 return false; 118 } 119 if ((inImage->numRows + y0in - y0sub) > subImage->numRows) { 120 psError(PS_ERR_UNKNOWN, true, "Image does not have enough rows for subtraction.\n"); 121 return false; 122 } 123 124 if (scale == 1.0) { 125 for (int i = 0; i < inImage->numRows; i++) { 126 for (int j = 0; j < inImage->numCols; j++) { 127 inImage->data.F32[i][j] -= subImage->data.F32[i+y0in-y0sub][j+x0in-x0sub]; 128 if (inMask && subMask) { 129 inMask->data.U8[i][j] |= subMask->data.U8[i+y0in-y0sub][j+x0in-x0sub]; 130 } 100 131 } 101 132 } 102 } 103 104 return(in); 105 } 106 107 108 /****************************************************************************** 109 psSubtractDarkFrame(): this routine will take as input the pmReadout for the 110 input image and a pmReadout for the dark image. The dark image is scaled and 111 subtracted in place from the input image. 112 113 XXX: Verify that the image and readout offsets are being used the right way. 114 115 XXX: Ensure that it does the correct thing with image size. 116 *****************************************************************************/ 117 static pmReadout *SubtractDarkFrame( 118 pmReadout *in, 119 const pmReadout *dark, 120 psF32 scale) 121 { 122 // XXX: When did the ->row0 and ->col0 offsets get coded? 123 if (fabs(scale) > FLT_EPSILON) { 124 for (psS32 i=0;i<in->image->numRows;i++) { 125 for (psS32 j=0;j<in->image->numCols;j++) { 126 in->image->data.F32[i][j]-= 127 (scale * dark->image->data.F32[i+in->row0-dark->row0][j+in->col0-dark->col0]); 128 129 if ((in->mask != NULL) && (dark->mask != NULL)) { 130 (in->mask->data.U8[i][j])|= 131 dark->mask->data.U8[i+in->row0-dark->row0][j+in->col0-dark->col0]; 133 } else { 134 for (int i = 0; i < inImage->numRows; i++) { 135 for (int j = 0; j < inImage->numCols; j++) { 136 inImage->data.F32[i][j] -= subImage->data.F32[i+y0in-y0sub][j+x0in-x0sub] * scale; 137 if (inMask && subMask) { 138 inMask->data.U8[i][j] |= subMask->data.U8[i+y0in-y0sub][j+x0in-x0sub]; 132 139 } 133 140 } 134 141 } 135 } else { 136 for (psS32 i=0;i<in->image->numRows;i++) { 137 for (psS32 j=0;j<in->image->numCols;j++) { 138 in->image->data.F32[i][j]-= 139 dark->image->data.F32[i+in->row0-dark->row0][j+in->col0-dark->col0]; 140 141 if ((in->mask != NULL) && (dark->mask != NULL)) { 142 (in->mask->data.U8[i][j])|= 143 dark->mask->data.U8[i+in->row0-dark->row0][j+in->col0-dark->col0]; 144 } 145 } 146 } 147 } 148 149 return(in); 150 } 151 142 } 143 144 return true; 145 } 146 147 148 #if 0 152 149 /****************************************************************************** 153 150 ImageSubtractScalar(): subtract a scalar from the input image. 154 151 155 XXX: Is there a psLib function for this? 152 XXX: Use a psLib function for this. 153 154 XXX: This should 156 155 *****************************************************************************/ 157 static psImage *ImageSubtractScalar( 158 psImage *image, 159 psF32 scalar) 156 static psImage *ImageSubtractScalar(psImage *image, 157 psF32 scalar) 160 158 { 161 159 for (psS32 i=0;i<image->numRows;i++) { … … 166 164 return(image); 167 165 } 166 #endif 168 167 169 168 /****************************************************************************** … … 179 178 psStatsOptions opt = 0; 180 179 181 /*182 if (stat->options & PS_STAT_ROBUST_MODE) {183 if (numOptions == 0) {184 opt = PS_STAT_ROBUST_MODE;185 }186 numOptions++;187 }188 */189 180 if (stat->options & PS_STAT_ROBUST_MEDIAN) { 190 181 if (numOptions == 0) { … … 194 185 } 195 186 196 if (stat->options & PS_STAT_FITTED_MEAN) {197 if (numOptions == 0) {198 opt = PS_STAT_FITTED_MEAN;199 }200 numOptions++;201 }202 203 187 if (stat->options & PS_STAT_CLIPPED_MEAN) { 204 188 if (numOptions == 0) { … … 222 206 223 207 if (numOptions == 0) { 224 psError(PS_ERR_UNKNOWN,true, "No allowablestatistics options have been specified.\n");208 psError(PS_ERR_UNKNOWN,true, "No statistics options have been specified.\n"); 225 209 } 226 210 if (numOptions != 1) { … … 231 215 } 232 216 233 /****************************************************************************** 234 Polynomial1DCopy(): This private function copies the members of the existing 235 psPolynomial1D "in" into the existing psPolynomial1D "out". The previous 236 members of the existing psPolynomial1D "out" are psFree'ed. 237 *****************************************************************************/ 238 static psBool Polynomial1DCopy( 239 psPolynomial1D *out, 240 psPolynomial1D *in) 241 { 242 psFree(out->coeff); 243 psFree(out->coeffErr); 244 psFree(out->mask); 245 246 out->type = in->type; 247 out->nX = in->nX; 248 249 out->coeff = (psF64 *) psAlloc((in->nX + 1) * sizeof(psF64)); 250 // XXX: use memcpy 251 for (psS32 i = 0 ; i < (in->nX + 1) ; i++) { 252 out->coeff[i] = in->coeff[i]; 253 } 254 255 out->coeffErr = (psF64 *) psAlloc((in->nX + 1) * sizeof(psF64)); 256 // XXX: use memcpy 257 for (psS32 i = 0 ; i < (in->nX + 1) ; i++) { 258 out->coeffErr[i] = in->coeffErr[i]; 259 } 260 261 out->mask = (psMaskType *) psAlloc((in->nX + 1) * sizeof(psMaskType)); 262 // XXX: use memcpy 263 for (psS32 i = 0 ; i < (in->nX + 1) ; i++) { 264 out->mask[i] = in->mask[i]; 265 } 266 267 return(true); 268 } 269 270 /****************************************************************************** 271 Polynomial1DDup(): This private function duplicates and then returns the input 272 psPolynomial1D "in". 273 *****************************************************************************/ 274 static psPolynomial1D *Polynomial1DDup( 275 psPolynomial1D *in) 276 { 277 psPolynomial1D *out = psPolynomial1DAlloc(in->type, in->nX); 278 Polynomial1DCopy(out, in); 279 return(out); 280 } 281 282 283 /****************************************************************************** 284 SplineCopy(): This private function copies the members of the existing 285 psSpline in into the existing psSpline out. 286 *****************************************************************************/ 287 static psBool SplineCopy( 288 psSpline1D *out, 289 psSpline1D *in) 290 { 291 PS_ASSERT_PTR_NON_NULL(out, false); 292 PS_ASSERT_PTR_NON_NULL(in, false); 293 294 for (psS32 i = 0 ; i < out->n ; i++) { 295 psFree(out->spline[i]); 296 } 297 psFree(out->spline); 298 psFree(out->knots); 299 psFree(out->p_psDeriv2); 300 301 out->n = in->n; 302 out->spline = (psPolynomial1D **) psAlloc(in->n * sizeof(psPolynomial1D *)); 303 for (psS32 i = 0 ; i < in->n ; i++) { 304 out->spline[i] = Polynomial1DDup(in->spline[i]); 305 } 306 307 // XXX: use psVectorCopy if they get it working. 308 out->knots = psVectorAlloc(in->knots->n, in->knots->type.type); 309 out->knots->n = out->knots->nalloc; 310 for (psS32 i = 0 ; i < in->knots->n ; i++) { 311 out->knots->data.F32[i] = in->knots->data.F32[i]; 312 } 313 /* 314 out->knots = psVectorCopy(out->knots, in->knots, in->knots->type.type); 315 */ 316 317 out->p_psDeriv2 = (psF32 *) psAlloc((in->n + 1) * sizeof(psF32)); 318 // XXX: use memcpy 319 for (psS32 i = 0 ; i < (in->n + 1) ; i++) { 320 out->p_psDeriv2[i] = in->p_psDeriv2[i]; 321 } 322 323 return(true); 324 } 325 217 218 219 #if 0 326 220 /****************************************************************************** 327 221 ScaleOverscanVector(): this routine takes as input an arbitrary vector, … … 330 224 PM_FIT_POLYNOMIAL: fit a polynomial to the entire input vector data. 331 225 PM_FIT_SPLINE: fit splines to the input vector data. 332 The resulting spline or polynomial is set in the fitSpec argument. 226 XXX: Doesn't it make more sense to do polynomial interpolation on a few 227 elements of the input vector, rather than fit a polynomial to the entire 228 vector? 333 229 *****************************************************************************/ 334 static psVector *ScaleOverscanVector( 335 psVector *overscanVector, 336 psS32 n, 337 void *fitSpec, 338 pmFit fit) 230 static psVector *ScaleOverscanVector(psVector *overscanVector, 231 psS32 n, 232 void *fitSpec, 233 pmFit fit) 339 234 { 340 235 psTrace(".psModule.pmSubtracBias.ScaleOverscanVector", 4, 341 236 "---- ScaleOverscanVector() begin (%d -> %d) ----\n", overscanVector->n, n); 237 // PS_VECTOR_PRINT_F32(overscanVector); 342 238 343 239 if (NULL == overscanVector) { … … 347 243 // Allocate the new vector. 348 244 psVector *newVec = psVectorAlloc(n, PS_TYPE_F32); 349 newVec->n = newVec->nalloc; 245 350 246 // 351 247 // If the new vector is the same size as the old, simply copy the data. 352 248 // 353 249 if (n == overscanVector->n) { 354 return(psVectorCopy(newVec, overscanVector, PS_TYPE_F32)); 355 } 250 for (psS32 i = 0 ; i < n ; i++) { 251 newVec->data.F32[i] = overscanVector->data.F32[i]; 252 } 253 return(newVec); 254 } 255 psPolynomial1D *myPoly; 256 psSpline1D *mySpline; 356 257 psF32 x; 357 258 psS32 i; 358 259 if (fit == PM_FIT_POLYNOMIAL) { 359 260 // Fit a polynomial to the old overscan vector. 360 psPolynomial1D *myPoly = (psPolynomial1D *) fitSpec;261 myPoly = (psPolynomial1D *) fitSpec; 361 262 PS_ASSERT_POLY_NON_NULL(myPoly, NULL); 362 PS_ASSERT_POLY1D(myPoly, NULL);363 263 myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, overscanVector, NULL, NULL); 364 264 if (myPoly == NULL) { 365 psError(PS_ERR_UNKNOWN, false, " Could not fit a polynomial to the psVector.\n");265 psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector()(1): Could not fit a polynomial to the psVector.\n"); 366 266 return(NULL); 367 267 } … … 370 270 // of the old vector, use the fitted polynomial to determine the 371 271 // interpolated value at that point, and set the new vector. 372 for ( psS32i=0;i<n;i++) {272 for (i=0;i<n;i++) { 373 273 x = ((psF32) i) * ((psF32) overscanVector->n) / ((psF32) n); 374 274 newVec->data.F32[i] = psPolynomial1DEval(myPoly, x); 375 275 } 376 276 } else if (fit == PM_FIT_SPLINE) { 277 psS32 mustFreeSpline = 0; 278 // Fit a spline to the old overscan vector. 279 mySpline = (psSpline1D *) fitSpec; 280 // XXX: Does it make any sense to have a psSpline argument? 281 if (mySpline == NULL) { 282 mustFreeSpline = 1; 283 } 284 377 285 // 378 286 // NOTE: Since the X arg in the psVectorFitSpline1D() function is NULL, … … 380 288 // properly when doing the spline eval. 381 289 // 382 psSpline1D *mySpline = psVectorFitSpline1D(NULL, overscanVector); 290 // mySpline = psVectorFitSpline1D(mySpline, NULL, overscanVector, NULL); 291 mySpline = psVectorFitSpline1D(NULL, overscanVector); 383 292 if (mySpline == NULL) { 384 psError(PS_ERR_UNKNOWN, false, " Could not fit a spline to the psVector.\n");293 psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector()(2): Could not fit a spline to the psVector.\n"); 385 294 return(NULL); 386 295 } 296 // PS_PRINT_SPLINE(mySpline); 387 297 388 298 // For each element of the new vector, convert the x-ordinate to that 389 // of the old vector, use the fitted splineto determine the299 // of the old vector, use the fitted polynomial to determine the 390 300 // interpolated value at that point, and set the new vector. 391 for ( psS32i=0;i<n;i++) {301 for (i=0;i<n;i++) { 392 302 // Scale to [0 : overscanVector->n - 1] 393 303 x = ((psF32) i) * ((psF32) (overscanVector->n-1)) / ((psF32) n); 394 304 newVec->data.F32[i] = psSpline1DEval(mySpline, x); 395 305 } 396 397 psSpline1D *ptrSpline = (psSpline1D *) fitSpec; 398 if (ptrSpline != NULL) { 399 // Copy the resulting spline fit into ptrSpline. 400 PS_ASSERT_SPLINE(ptrSpline, NULL); 401 SplineCopy(ptrSpline, mySpline); 402 } 403 psFree(mySpline); 306 if (mustFreeSpline ==1) { 307 psFree(mySpline); 308 } 309 // PS_VECTOR_PRINT_F32(newVec); 310 311 404 312 } else { 405 313 psError(PS_ERR_UNKNOWN, true, "unknown fit type. Returning NULL.\n"); … … 413 321 } 414 322 323 #endif 324 325 // Produce an overscan vector from an array of pixels 326 static psVector *overscanVector(pmOverscanOptions *overscanOpts, // Overscan options 327 const psArray *pixels, // Array of vectors containing the pixel values 328 psStats *myStats // Statistic to use in reducing the overscan 329 ) 330 { 331 // Reduce the overscans 332 psVector *reduced = psVectorAlloc(pixels->n, PS_TYPE_F32); // Overscan for each row 333 psVector *ordinate = psVectorAlloc(pixels->n, PS_TYPE_F32); // Ordinate 334 psVector *mask = psVectorAlloc(pixels->n, PS_TYPE_U8); // Mask for fitting 335 for (int i = 0; i < pixels->n; i++) { 336 psVector *values = pixels->data[i]; // Vector with overscan values 337 if (values->n > 0) { 338 mask->data.U8[i] = 0; 339 ordinate->data.F32[i] = 2.0*(float)i/(float)pixels->n - 1.0; // Scale to [-1,1] 340 psVectorStats(myStats, values, NULL, NULL, 0); 341 double reducedVal = NAN; // Result of statistics 342 if (! p_psGetStatValue(myStats, &reducedVal)) { 343 psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result " 344 "of statistics on row %d.\n", i); 345 return NULL; 346 } 347 reduced->data.F32[i] = reducedVal; 348 } else if (overscanOpts->fitType == PM_FIT_NONE) { 349 psError(PS_ERR_UNKNOWN, true, "The overscan is not supplied for all points on the " 350 "image, and no fit is requested.\n"); 351 return NULL; 352 } else { 353 // We'll fit this one out 354 mask->data.U8[i] = 1; 355 } 356 } 357 358 // Fit the overscan, if required 359 switch (overscanOpts->fitType) { 360 case PM_FIT_NONE: 361 // No fitting --- that's easy. 362 break; 363 case PM_FIT_POLY_ORD: 364 if (overscanOpts->poly && (overscanOpts->poly->nX != overscanOpts->order || 365 overscanOpts->poly->type != PS_POLYNOMIAL_ORD)) { 366 psFree(overscanOpts->poly); 367 overscanOpts->poly = NULL; 368 } 369 if (! overscanOpts->poly) { 370 overscanOpts->poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, overscanOpts->order); 371 } 372 psVectorFitPolynomial1D(overscanOpts->poly, mask, 1, reduced, NULL, ordinate); 373 psFree(reduced); 374 reduced = psPolynomial1DEvalVector(overscanOpts->poly, ordinate); 375 break; 376 case PM_FIT_POLY_CHEBY: 377 if (overscanOpts->poly && (overscanOpts->poly->nX != overscanOpts->order || 378 overscanOpts->poly->type != PS_POLYNOMIAL_CHEB)) { 379 psFree(overscanOpts->poly); 380 overscanOpts->poly = NULL; 381 } 382 if (! overscanOpts->poly) { 383 overscanOpts->poly = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, overscanOpts->order); 384 } 385 psVectorFitPolynomial1D(overscanOpts->poly, mask, 1, reduced, NULL, ordinate); 386 psFree(reduced); 387 reduced = psPolynomial1DEvalVector(overscanOpts->poly, ordinate); 388 break; 389 case PM_FIT_SPLINE: 390 // XXX I don't think psSpline1D is up to scratch yet --- it has no mask, and requires an 391 // input spline 392 overscanOpts->spline = psVectorFitSpline1D(reduced, ordinate); 393 psFree(reduced); 394 reduced = psSpline1DEvalVector(overscanOpts->spline, ordinate); 395 break; 396 default: 397 psError(PS_ERR_UNKNOWN, true, "Unknown value for the fitting type: %d\n", overscanOpts->fitType); 398 return NULL; 399 break; 400 } 401 402 psFree(ordinate); 403 psFree(mask); 404 405 return reduced; 406 } 407 408 409 415 410 /****************************************************************************** 411 XXX: The SDRS does not specify type support. F32 is implemented here. 416 412 *****************************************************************************/ 417 static psS32 GetOverscanSize( 418 psImage *inImg, 419 pmOverscanAxis overScanAxis) 420 { 421 if (overScanAxis == PM_OVERSCAN_ROWS) { 422 return(inImg->numCols); 423 } else if (overScanAxis == PM_OVERSCAN_COLUMNS) { 424 return(inImg->numRows); 425 } else if (overScanAxis == PM_OVERSCAN_ALL) { 426 return(1); 427 } 428 return(0); 429 } 430 431 /****************************************************************************** 432 GetOverscanAxis(in) this private routine determines the appropiate overscan 433 axis from the parent cell metadata. 434 435 XXX: Verify the READDIR corresponds with my overscan axis. 436 *****************************************************************************/ 437 static pmOverscanAxis GetOverscanAxis(pmReadout *in) 438 { 439 psBool rc; 440 if ((in->parent != NULL) && (in->parent->concepts)) { 441 psS32 dir = psMetadataLookupS32(&rc, in->parent->concepts, "CELL.READDIR"); 442 if (rc == true) { 443 if (dir == 1) { 444 return(PM_OVERSCAN_ROWS); 445 } else if (dir == 2) { 446 return(PM_OVERSCAN_COLUMNS); 447 } else if (dir == 3) { 448 return(PM_OVERSCAN_ALL); 449 } 450 } 451 } 452 453 psLogMsg(__func__, PS_LOG_WARN, 454 "WARNING: pmSubtractBias.(): could not determine CELL.READDIR from in->parent metadata. Setting overscan axis to PM_OVERSCAN_NONE.\n"); 455 return(PM_OVERSCAN_NONE); 456 } 457 458 /****************************************************************************** 459 my_psListLength(list): determine the length of a psList. 460 461 XXX: Put this elsewhere. 462 463 XXX: psList.h now has a version of this function. Use that instead. 464 *****************************************************************************/ 465 466 static psS32 my_psListLength( 467 psList *list) 468 { 469 psS32 length = 0; 470 psListElem *tmpElem = (psListElem *) list->head; 471 while (NULL != tmpElem) { 472 tmpElem = tmpElem->next; 473 length++; 474 } 475 return(length); 476 } 477 478 /****************************************************************************** 479 Note: this isn't needed anymore as of psModule SDRS 12-09. 480 *****************************************************************************/ 481 static psBool OverscanReducePixel( 482 psImage *in, 483 psList *bias, 484 psStats *myStats) 485 { 486 PS_ASSERT_PTR_NON_NULL(in, NULL); 487 PS_ASSERT_PTR_NON_NULL(bias, NULL); 488 PS_ASSERT_PTR_NON_NULL(bias->head, NULL); 489 PS_ASSERT_PTR_NON_NULL(myStats, NULL); 490 491 // Allocate a psVector with one element per overscan image. 492 psS32 numOverscanImages = my_psListLength(bias); 493 psVector *statsAll = psVectorAlloc(numOverscanImages, PS_TYPE_F32); 494 statsAll->n = statsAll->nalloc; 495 psListElem *tmpOverscan = (psListElem *) bias->head; 496 psS32 i = 0; 497 psF64 statValue; 498 // 499 // We loop through each overscan image, calculating the specified 500 // statistic on that image. 501 // 502 while (NULL != tmpOverscan) { 503 psImage *myOverscanImage = (psImage *) tmpOverscan->data; 504 505 PS_ASSERT_IMAGE_TYPE(myOverscanImage, PS_TYPE_F32, NULL); 506 myStats = psImageStats(myStats, myOverscanImage, NULL, (psMaskType)0xffffffff); 507 if (myStats == NULL) { 508 psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation. Returning in image.\n"); 509 psFree(statsAll); 510 return(false); 511 } 512 if (false == p_psGetStatValue(myStats, &statValue)) { 513 psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation. Returning in image.\n"); 514 psFree(statsAll); 515 return(false); 516 } 517 statsAll->data.F32[i] = statValue; 518 i++; 519 tmpOverscan = tmpOverscan->next; 520 } 521 522 // 523 // We reduce the individual stats for each overscan image to 524 // a single psF32. 525 // 526 myStats = psVectorStats(myStats, statsAll, NULL, NULL, 0); 527 if (myStats == NULL) { 528 psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation. Returning in image.\n"); 529 psFree(statsAll); 530 return(false); 531 } 532 if (false == p_psGetStatValue(myStats, &statValue)) { 533 psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation. Returning in image.\n"); 534 psFree(statsAll); 535 return(false); 536 } 537 538 // 539 // Subtract the result and return. 540 // 541 ImageSubtractScalar(in, statValue); 542 psFree(statsAll); 543 return(in); 544 } 545 546 /****************************************************************************** 547 ReduceOverscanImageToCol(overscanImage, myStats): This private routine reduces 548 a single psImage to a column by combining all pixels from each row into a 549 single pixel via requested statistic in myStats. 550 *****************************************************************************/ 551 static psVector *ReduceOverscanImageToCol( 552 psImage *overscanImage, 553 psStats *myStats) 554 { 555 psF64 statValue; 556 psVector *tmpRow = psVectorAlloc(overscanImage->numCols, PS_TYPE_F32); 557 psVector *tmpCol = psVectorAlloc(overscanImage->numRows, PS_TYPE_F32); 558 tmpRow->n = tmpRow->nalloc; 559 tmpCol->n = tmpCol->nalloc; 560 561 // 562 // For each row, we store all pixels in that row into a temporary psVector, 563 // then we run psVectorStats() on that vector. 564 // 565 for (psS32 i=0;i<overscanImage->numRows;i++) { 566 for (psS32 j=0;j<overscanImage->numCols;j++) { 567 tmpRow->data.F32[j] = overscanImage->data.F32[i][j]; 568 } 569 570 psStats *rc = psVectorStats(myStats, tmpRow, NULL, NULL, 0); 571 if (rc == NULL) { 572 psError(PS_ERR_UNKNOWN, true, "psVectorStats() could not perform requested statistical operation. Returning in image.\n"); 573 return(NULL); 574 } 575 576 if (false == p_psGetStatValue(rc, &statValue)) { 577 psError(PS_ERR_UNKNOWN, true, "p_psGetStatValue() could not determine result from requested statistical operation. Returning in image.\n"); 578 return(NULL); 579 } 580 581 tmpCol->data.F32[i] = (psF32) statValue; 582 } 583 psFree(tmpRow); 584 585 return(tmpCol); 586 } 587 588 /****************************************************************************** 589 ReduceOverscanImageToCol(overscanImage, myStats): This private routine reduces 590 a single psImage to a row by combining all pixels from each column into a 591 single pixel via requested statistic in myStats. 592 *****************************************************************************/ 593 static psVector *ReduceOverscanImageToRow( 594 psImage *overscanImage, 595 psStats *myStats) 596 { 597 psF64 statValue; 598 psVector *tmpRow = psVectorAlloc(overscanImage->numCols, PS_TYPE_F32); 599 psVector *tmpCol = psVectorAlloc(overscanImage->numRows, PS_TYPE_F32); 600 tmpRow->n = tmpRow->nalloc; 601 tmpCol->n = tmpCol->nalloc; 602 603 // 604 // For each column, we store all pixels in that column into a temporary psVector, 605 // then we run psVectorStats() on that vector. 606 // 607 for (psS32 i=0;i<overscanImage->numCols;i++) { 608 for (psS32 j=0;j<overscanImage->numRows;j++) { 609 tmpCol->data.F32[j] = overscanImage->data.F32[j][i]; 610 } 611 612 psStats *rc = psVectorStats(myStats, tmpCol, NULL, NULL, 0); 613 if (rc == NULL) { 614 psError(PS_ERR_UNKNOWN, true, "psVectorStats() could not perform requested statistical operation. Returning in image.\n"); 615 return(NULL); 616 } 617 618 if (false == p_psGetStatValue(rc, &statValue)) { 619 psError(PS_ERR_UNKNOWN, true, "p_psGetStatValue() could not determine result from requested statistical operation. Returning in image.\n"); 620 return(NULL); 621 } 622 623 tmpRow->data.F32[i] = (psF32) statValue; 624 } 625 psFree(tmpCol); 626 627 return(tmpRow); 628 } 629 630 /****************************************************************************** 631 OverscanReduce(vecSize, bias, myStats): This private routine takes a psList of 632 overscan images (in bias) and reduces them to a single psVector via the 633 specified psStats struct. The vector is then scaled to the length or the 634 row/column in inImg. 635 *****************************************************************************/ 636 static psVector* OverscanReduce( 637 psImage *inImg, 638 pmOverscanAxis overScanAxis, 639 psList *bias, 640 void *fitSpec, 641 pmFit fit, 642 psStats *myStats) 643 { 644 if ((overScanAxis != PM_OVERSCAN_ROWS) && (overScanAxis != PM_OVERSCAN_COLUMNS)) { 645 psError(PS_ERR_UNKNOWN, true, "overScanAxis must be PM_OVERSCAN_ROWS or PM_OVERSCAN_COLUMNS\n"); 646 return(NULL); 647 } 648 PS_ASSERT_PTR_NON_NULL(inImg, NULL); 649 PS_ASSERT_PTR_NON_NULL(bias, NULL); 650 PS_ASSERT_PTR_NON_NULL(bias->head, NULL); 651 PS_ASSERT_PTR_NON_NULL(myStats, NULL); 652 // 653 // Allocate a psVector for the output of this routine. 654 // 655 psS32 vecSize = GetOverscanSize(inImg, overScanAxis); 656 psVector *overscanVector = psVectorAlloc(vecSize, PS_TYPE_F32); 657 overscanVector->n = overscanVector->nalloc; 658 659 // 660 // Allocate an array of psVectors with one psVector per element of the 661 // final oversan column vector. These psVectors will be used with 662 // psStats to reduce the multiple elements from each overscan column 663 // vector to a single final column vector. 664 // 665 psS32 numOverscanImages = my_psListLength(bias); 666 psVector **overscanVectors = (psVector **) psAlloc(numOverscanImages * sizeof(psVector *)); 667 // (*overscanVectors)->n = (*overscanVectors)->nalloc; 668 for (psS32 i = 0 ; i < numOverscanImages ; i++) { 669 overscanVectors[i] = NULL; 670 } 671 672 // 673 // We iterate through the list of overscan images. For each image, 674 // we reduce it to a single column or row. Save the overscan vector 675 // in overscanVectors[]. 676 // 677 psListElem *tmpOverscan = (psListElem *) bias->head; 678 psS32 overscanID = 0; 679 while (tmpOverscan != NULL) { 680 psImage *tmpOverscanImage = (psImage *) tmpOverscan->data; 681 if (overScanAxis == PM_OVERSCAN_ROWS) { 682 overscanVectors[overscanID] = ReduceOverscanImageToRow(tmpOverscanImage, myStats); 683 } else if (overScanAxis == PM_OVERSCAN_COLUMNS) { 684 overscanVectors[overscanID] = ReduceOverscanImageToCol(tmpOverscanImage, myStats); 685 } 686 687 tmpOverscan = tmpOverscan->next; 688 overscanID++; 689 } 690 691 // 692 // For each overscan vector, if necessary, we scale that column or 693 // row to vecSize. Note: we should have already ensured that the 694 // fit is poly or spline. 695 // 696 for (psS32 i = 0 ; i < numOverscanImages ; i++) { 697 psVector *tmpOverscanVector = overscanVectors[i]; 698 699 if (tmpOverscanVector->n != vecSize) { 700 overscanVectors[i] = ScaleOverscanVector(tmpOverscanVector, vecSize, fitSpec, fit); 701 if (overscanVectors[i] == NULL) { 702 psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector(): could not scale the overscan vector.\n"); 703 for (psS32 i = 0 ; i < numOverscanImages ; i++) { 704 psFree(overscanVectors[i]); 705 } 706 psFree(overscanVectors); 707 psFree(tmpOverscanVector); 708 return(NULL); 709 } 710 psFree(tmpOverscanVector); 711 } 712 } 713 714 // 715 // We collect all elements in the overscan vectors for the various 716 // overscan images into a single psVector (tmpVec). Then we call 717 // psStats on that vector to determine the final values for the 718 // overscan vector. 719 // 720 psVector *tmpVec = psVectorAlloc(numOverscanImages, PS_TYPE_F32); 721 tmpVec->n = tmpVec->nalloc; 722 psF64 statValue; 723 for (psS32 i = 0 ; i < vecSize ; i++) { 724 // Collect the i-th elements from each overscan vector into a single vector. 725 for (psS32 j = 0 ; j < numOverscanImages ; j++) { 726 tmpVec->data.F32[j] = overscanVectors[j]->data.F32[i]; 727 } 728 729 if (NULL == psVectorStats(myStats, tmpVec, NULL, NULL, 0)) { 730 psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation. Returning in image.\n"); 731 for (psS32 i = 0 ; i < numOverscanImages ; i++) { 732 psFree(overscanVectors[i]); 733 } 734 psFree(overscanVectors); 735 psFree(tmpVec); 736 return(NULL); 737 } 738 if (false == p_psGetStatValue(myStats, &statValue)) { 739 psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation. Returning in image.\n"); 740 for (psS32 i = 0 ; i < numOverscanImages ; i++) { 741 psFree(overscanVectors[i]); 742 } 743 psFree(overscanVectors); 744 psFree(tmpVec); 745 return(NULL); 746 } 747 748 overscanVector->data.F32[i] = (psF32) statValue; 749 } 750 751 // 752 // We're done. Free the intermediate overscan vectors. 753 // 754 psFree(tmpVec); 755 for (psS32 i = 0 ; i < numOverscanImages ; i++) { 756 psFree(overscanVectors[i]); 757 } 758 psFree(overscanVectors); 759 760 // 761 // Return the computed overscanVector 762 // 763 return(overscanVector); 764 } 765 766 /****************************************************************************** 767 RebinOverscanVector(overscanVector, nBinOrig, myStats): this private routine 768 takes groups of nBinOrig elements in the input vector, combines them into a 769 single pixel via myStats and psVectorStats(), and then outputs a vector of 770 those pixels. 771 *****************************************************************************/ 772 static psS32 RebinOverscanVector( 773 psVector *overscanVector, 774 psS32 nBinOrig, 775 psStats *myStats) 776 { 777 psF64 statValue; 778 psS32 nBin; 779 if ((nBinOrig > 1) && (nBinOrig < overscanVector->n)) { 780 psS32 numBins = 1+((overscanVector->n)/nBinOrig); 781 psVector *myBin = psVectorAlloc(numBins, PS_TYPE_F32); 782 psVector *binVec = psVectorAlloc(nBinOrig, PS_TYPE_F32); 783 myBin->n = myBin->nalloc; 784 binVec->n = binVec->nalloc; 785 786 for (psS32 i=0;i<numBins;i++) { 787 for(psS32 j=0;j<nBinOrig;j++) { 788 if (overscanVector->n > ((i*nBinOrig)+j)) { 789 binVec->data.F32[j] = overscanVector->data.F32[(i*nBinOrig)+j]; 790 } else { 791 // XXX: we get here if nBinOrig does not evenly divide 792 // the overscanVector vector. This is the last bin. Should 793 // we change the binVec->n to acknowledge that? 794 binVec->n = j; 795 } 796 } 797 psStats *rc = psVectorStats(myStats, binVec, NULL, NULL, 0); 798 if (rc == NULL) { 799 psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation. Returning in image.\n"); 800 return(-1); 801 } 802 if (false == p_psGetStatValue(rc, &statValue)) { 803 psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation. Returning in image.\n"); 804 return(-1); 805 } 806 myBin->data.F32[i] = statValue; 807 } 808 809 // Change the effective size of overscanVector. 810 overscanVector->n = numBins; 811 for (psS32 i=0;i<numBins;i++) { 812 overscanVector->data.F32[i] = myBin->data.F32[i]; 813 } 814 psFree(binVec); 815 psFree(myBin); 816 nBin = nBinOrig; 817 } else { 818 nBin = 1; 819 } 820 821 return(nBin); 822 } 823 824 /****************************************************************************** 825 FitOverscanVectorAndUnbin(inImg, overscanVector, overScanAxis, fitSpec, fit, 826 nBin): this private routine fits a psPolynomial or psSpline to the overscan 827 vector. It then creates a new vector, with a size determined by the input 828 image, evaluates the psPolynomial or psSpline at each element in that vector, 829 then returns that vector. 830 *****************************************************************************/ 831 static psVector *FitOverscanVectorAndUnbin( 832 psImage *inImg, 833 psVector *overscanVector, 834 pmOverscanAxis overScanAxis, 835 void *fitSpec, 836 pmFit fit, 837 psS32 nBin) 838 { 839 psPolynomial1D* myPoly = NULL; 840 psSpline1D *mySpline = NULL; 841 // 842 // Fit a polynomial or spline to the overscan vector. 843 // 844 if (fit == PM_FIT_POLYNOMIAL) { 845 myPoly = (psPolynomial1D *) fitSpec; 846 PS_ASSERT_POLY_NON_NULL(myPoly, NULL); 847 PS_ASSERT_POLY1D(myPoly, NULL); 848 myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, overscanVector, NULL, NULL); 849 if (myPoly == NULL) { 850 psError(PS_ERR_UNKNOWN, false, "Could not fit a polynomial to overscan vector. Returning NULL.\n"); 851 return(NULL); 852 } 853 } else if (fit == PM_FIT_SPLINE) { 854 mySpline = psVectorFitSpline1D(NULL, overscanVector); 855 if (mySpline == NULL) { 856 psError(PS_ERR_UNKNOWN, false, "Could not fit a spline to overscan vector. Returning NULL.\n"); 857 return(NULL); 858 } 859 if (fitSpec != NULL) { 860 // Copy the resulting spline fit into fitSpec. 861 psSpline1D *ptrSpline = (psSpline1D *) fitSpec; 862 PS_ASSERT_SPLINE(ptrSpline, NULL); 863 SplineCopy(ptrSpline, mySpline); 864 } 865 } 866 867 // 868 // Evaluate the poly/spline at each pixel in the overscan row/column. 869 // 870 psS32 vecSize = GetOverscanSize(inImg, overScanAxis); 871 psVector *newVec = psVectorAlloc(vecSize, PS_TYPE_F32); 872 newVec->n = newVec->nalloc; 873 if ((nBin > 1) && (nBin < overscanVector->n)) { 874 for (psS32 i = 0 ; i < vecSize ; i++) { 875 if (fit == PM_FIT_POLYNOMIAL) { 876 newVec->data.F32[i] = psPolynomial1DEval(myPoly, ((psF32) i) / ((psF32) nBin)); 877 } else if (fit == PM_FIT_SPLINE) { 878 newVec->data.F32[i] = psSpline1DEval(mySpline, ((psF32) i) / ((psF32) nBin)); 879 } 880 } 881 } else { 882 for (psS32 i = 0 ; i < vecSize ; i++) { 883 if (fit == PM_FIT_POLYNOMIAL) { 884 newVec->data.F32[i] = psPolynomial1DEval(myPoly, (psF32) i); 885 } else if (fit == PM_FIT_SPLINE) { 886 newVec->data.F32[i] = psSpline1DEval(mySpline, (psF32) i); 887 } 888 } 889 } 890 891 psFree(mySpline); 892 psFree(overscanVector); 893 return(newVec); 894 } 895 896 897 898 /****************************************************************************** 899 UnbinOverscanVector(inImg, overscanVector, overScanAxis, nBin): this private 900 routine takes a psVector overscanVector that was previously binned by a factor 901 of nBin, and then expands it to its original size, duplicated elements nBin 902 times for each element in the input vector overscanVector. 903 *****************************************************************************/ 904 static psVector *UnbinOverscanVector( 905 psImage *inImg, 906 psVector *overscanVector, 907 pmOverscanAxis overScanAxis, 908 psS32 nBin) 909 { 910 psS32 vecSize = 0; 911 912 if (overScanAxis == PM_OVERSCAN_ROWS) { 913 vecSize = inImg->numCols; 914 } else if (overScanAxis == PM_OVERSCAN_COLUMNS) { 915 vecSize = inImg->numRows; 916 } 917 918 psVector *newVec = psVectorAlloc(vecSize, PS_TYPE_F32); 919 newVec->n = newVec->nalloc; 920 for (psS32 i = 0 ; i < vecSize ; i++) { 921 newVec->data.F32[i] = overscanVector->data.F32[i/nBin]; 922 } 923 924 psFree(overscanVector); 925 return(newVec); 926 } 927 928 929 /****************************************************************************** 930 SubtractVectorFromImage(inImg, overscanVector, overScanAxis): this private 931 routine subtracts the overscanVector column-wise or row-wise from inImg. 932 *****************************************************************************/ 933 static psImage *SubtractVectorFromImage( 934 psImage *inImg, 935 psVector *overscanVector, 936 pmOverscanAxis overScanAxis) 937 { 938 // 939 // Subtract overscan vector row-wise from the image. 940 // 941 if (overScanAxis == PM_OVERSCAN_ROWS) { 942 for (psS32 i=0;i<inImg->numCols;i++) { 943 for (psS32 j=0;j<inImg->numRows;j++) { 944 inImg->data.F32[j][i]-= overscanVector->data.F32[i]; 945 } 946 } 947 } 948 949 // 950 // Subtract overscan vector column-wise from the image. 951 // 952 if (overScanAxis == PM_OVERSCAN_COLUMNS) { 953 for (psS32 i=0;i<inImg->numRows;i++) { 954 for (psS32 j=0;j<inImg->numCols;j++) { 955 inImg->data.F32[i][j]-= overscanVector->data.F32[i]; 956 } 957 } 958 } 959 960 return(inImg); 961 } 962 963 964 965 typedef enum { 966 PM_ERROR_NO_SUBTRACTION, 967 PM_WARNING_NO_SUBTRACTION, 968 PM_ERROR_NO_BIAS_SUBTRACT, 969 PM_WARNING_NO_BIAS_SUBTRACT, 970 PM_ERROR_NO_DARK_SUBTRACT, 971 PM_WARNING_NO_DARK_SUBTRACT, 972 PM_OKAY 973 } pmSubtractBiasAssertStatus; 974 /****************************************************************************** 975 AssertCodeOverscan(....) this private routine verifies that the various input 976 parameters to pmSubtractBias() are correct for overscan subtraction. 977 *****************************************************************************/ 978 pmSubtractBiasAssertStatus AssertCodeOverscan( 979 pmReadout *in, 980 void *fitSpec, 981 pmFit fit, 982 bool overscan, 983 psStats *stat, 984 int nBinOrig, 985 const pmReadout *bias, 986 const pmReadout *dark) 987 { 988 989 PS_ASSERT_READOUT_NON_NULL(in, PM_ERROR_NO_SUBTRACTION); 990 PS_ASSERT_READOUT_NON_EMPTY(in, PM_ERROR_NO_SUBTRACTION); 991 PS_ASSERT_READOUT_TYPE(in, PS_TYPE_F32, PM_ERROR_NO_SUBTRACTION); 992 PS_WARN_PTR_NON_NULL(in->parent); 993 if (in->parent != NULL) { 994 PS_WARN_PTR_NON_NULL(in->parent->concepts); 995 } 996 997 if (overscan == true) { 998 pmOverscanAxis overScanAxis = GetOverscanAxis(in); 999 PS_ASSERT_PTR_NON_NULL(stat, PM_ERROR_NO_SUBTRACTION); 1000 PS_ASSERT_PTR_NON_NULL(in->bias, PM_ERROR_NO_SUBTRACTION); 1001 PS_ASSERT_PTR_NON_NULL(in->bias->head, PM_ERROR_NO_SUBTRACTION); 1002 // 1003 // Check the type, size of each bias image. 1004 // 1005 psListElem *tmpOverscan = (psListElem *) in->bias->head; 1006 psS32 numOverscans = 0; 1007 while (NULL != tmpOverscan) { 1008 numOverscans++; 1009 psImage *myOverscanImage = (psImage *) tmpOverscan->data; 1010 PS_ASSERT_IMAGE_TYPE(myOverscanImage, PS_TYPE_F32, PM_ERROR_NO_SUBTRACTION); 1011 // XXX: Get this right with the rows and columns. 1012 if (overScanAxis == PM_OVERSCAN_ROWS) { 1013 if (myOverscanImage->numRows != in->image->numRows) { 1014 psLogMsg(__func__, PS_LOG_WARN, 1015 "WARNING: pmSubtractBias.(): overscan image (# %d) has %d rows, input image has %d rows\n", 1016 numOverscans, myOverscanImage->numCols, in->image->numRows); 1017 if (fit == PM_FIT_NONE) { 1018 psError(PS_ERR_UNKNOWN, true, "Don't know how to scale the overscan vectors. Set fit to PM_FIT_POLYNOMIAL or PM_FIT_SPLINE.\n"); 1019 return(PM_ERROR_NO_SUBTRACTION); 1020 } 1021 } 1022 } else if (overScanAxis == PM_OVERSCAN_COLUMNS) { 1023 if (myOverscanImage->numCols != in->image->numCols) { 1024 psLogMsg(__func__, PS_LOG_WARN, 1025 "WARNING: pmSubtractBias.(): overscan image (# %d) has %d columns, input image has %d columns\n", 1026 numOverscans, myOverscanImage->numCols, in->image->numCols); 1027 if (fit == PM_FIT_NONE) { 1028 psError(PS_ERR_UNKNOWN, true, "Don't know how to scale the overscan vectors. Set fit to PM_FIT_POLYNOMIAL or PM_FIT_SPLINE.\n"); 1029 return(PM_ERROR_NO_SUBTRACTION); 1030 } 1031 } 1032 } else if (overScanAxis != PM_OVERSCAN_ALL) { 1033 psError(PS_ERR_UNKNOWN, true, "Must specify and overscan axis.\n"); 1034 return(PM_ERROR_NO_SUBTRACTION); 1035 } 1036 tmpOverscan = tmpOverscan->next; 1037 } 1038 } else { 1039 if (fit != PM_FIT_NONE) { 1040 psLogMsg(__func__, PS_LOG_WARN, 1041 "WARNING: pmSubtractBias.(): overscan is FALSE and fit is not PM_FIT_NONE.\n"); 1042 return(PM_WARNING_NO_SUBTRACTION); 1043 } 1044 } 1045 1046 // XXX: I do not like the following spec since it's useless to specify 1047 // a psSpline as the fitSpec. 1048 if (0) { 1049 if ((fitSpec == NULL) && 1050 ((fit != PM_FIT_NONE) || (overscan == true))) { 1051 psError(PS_ERR_UNKNOWN, true, "fitSpec is NULL and fit is not PM_FIT_NONE or overscan is TRUE.\n"); 1052 return(PM_ERROR_NO_SUBTRACTION); 1053 } 1054 } 1055 1056 return(PM_OKAY); 1057 } 1058 1059 /****************************************************************************** 1060 AssertCodeBias(....) this private routine verifies that the various input 1061 parameters to pmSubtractBias() are correct for bias subtraction. 1062 *****************************************************************************/ 1063 static pmSubtractBiasAssertStatus AssertCodeBias( 1064 pmReadout *in, 1065 void *fitSpec, 1066 pmFit fit, 1067 bool overscan, 1068 psStats *stat, 1069 int nBinOrig, 1070 const pmReadout *bias, 1071 const pmReadout *dark) 1072 { 1073 if ((in->image->numRows + in->row0 - bias->row0) > bias->image->numRows) { 1074 psError(PS_ERR_UNKNOWN,true, "bias image does not have enough rows. Returning in image\n"); 1075 return(PM_ERROR_NO_BIAS_SUBTRACT); 1076 } 1077 if ((in->image->numCols + in->col0 - bias->col0) > bias->image->numCols) { 1078 psError(PS_ERR_UNKNOWN,true, "bias image does not have enough columns. Returning in image\n"); 1079 return(PM_ERROR_NO_BIAS_SUBTRACT); 1080 } 1081 1082 if (bias != NULL) { 1083 PS_ASSERT_READOUT_NON_EMPTY(bias, PM_ERROR_NO_BIAS_SUBTRACT); 1084 PS_ASSERT_READOUT_TYPE(bias, PS_TYPE_F32, PM_ERROR_NO_DARK_SUBTRACT); 1085 } 1086 return(PM_OKAY); 1087 } 1088 1089 /****************************************************************************** 1090 AssertCodeDark(....) this private routine verifies that the various input 1091 parameters to pmSubtractBias() are correct for dark subtraction. 1092 *****************************************************************************/ 1093 pmSubtractBiasAssertStatus AssertCodeDark( 1094 pmReadout *in, 1095 void *fitSpec, 1096 pmFit fit, 1097 bool overscan, 1098 psStats *stat, 1099 int nBinOrig, 1100 const pmReadout *bias, 1101 const pmReadout *dark) 1102 { 1103 if ((in->image->numRows + in->row0 - dark->row0) > dark->image->numRows) { 1104 psError(PS_ERR_UNKNOWN, true, "dark image does not have enough rows. Returning in image\n"); 1105 return(PM_ERROR_NO_DARK_SUBTRACT); 1106 } 1107 if ((in->image->numCols + in->col0 - dark->col0) > dark->image->numCols) { 1108 psError(PS_ERR_UNKNOWN, true, "dark image does not have enough columns. Returning in image\n"); 1109 return(PM_ERROR_NO_DARK_SUBTRACT); 1110 } 1111 1112 if (dark != NULL) { 1113 PS_ASSERT_READOUT_NON_EMPTY(dark, PM_ERROR_NO_DARK_SUBTRACT); 1114 PS_ASSERT_READOUT_TYPE(dark, PS_TYPE_F32, PM_ERROR_NO_DARK_SUBTRACT); 1115 } 1116 return(PM_OKAY); 1117 } 1118 1119 /****************************************************************************** 1120 p_psDetermineTrimmedImage(): global routine: determines the region of the 1121 input pmReadout which will be operated on by the various detrend modules. It 1122 does a metadata fetch on "CELL.TRIMSEC" for the parent cell of the pmReadout. 1123 1124 Use it this way: 1125 PS_WARN_PTR_NON_NULL(in->parent); 1126 if (in->parent != NULL) { 1127 PS_WARN_PTR_NON_NULL(in->parent->concepts); 1128 } 1129 // 1130 // Determine trimmed image from metadata. 1131 // 1132 psImage *trimmedImg = p_psDetermineTrimmedImage(in); 1133 1134 XXX: Create a pmUtils.c file and put this routine there. 1135 *****************************************************************************/ 1136 psImage *p_psDetermineTrimmedImage(pmReadout *in) 1137 { 1138 if ((in->parent == NULL) || (in->parent->concepts == NULL)) { 1139 psLogMsg(__func__, PS_LOG_WARN, 1140 "WARNING: could not determine CELL.TRIMSEC from parent cell Metadata (NULL).\n"); 1141 return(in->image); 1142 } 1143 1144 psBool rc = false; 1145 psImage *trimmedImg = NULL; 1146 psRegion *trimRegion = psMetadataLookupPtr(&rc, in->parent->concepts, 1147 "CELL.TRIMSEC"); 1148 if (rc == false) { 1149 psLogMsg(__func__, PS_LOG_WARN, 1150 "WARNING: could not determine CELL.TRIMSEC from parent cell Metadata.\n"); 1151 trimmedImg = in->image; 1152 } else { 1153 trimmedImg = psImageSubset(in->image, *trimRegion); 1154 } 1155 1156 return(trimmedImg); 1157 } 1158 1159 1160 /****************************************************************************** 1161 pmSubtractBias(....): see SDRS for complete specification. 1162 1163 XXX: Code and assert type support: U16, S32, F32. 1164 XXX: Add trace messages. 1165 *****************************************************************************/ 1166 pmReadout *pmSubtractBias( 1167 pmReadout *in, 1168 void *fitSpec, 1169 pmFit fit, 1170 bool overscan, 1171 psStats *stat, 1172 int nBin, 1173 const pmReadout *bias, 1174 const pmReadout *dark) 413 pmReadout *pmSubtractBias(pmReadout *in, pmOverscanOptions *overscanOpts, 414 const pmReadout *bias, const pmReadout *dark) 1175 415 { 1176 416 psTrace(".psModule.pmSubtracBias.pmSubtractBias", 4, 1177 417 "---- pmSubtractBias() begin ----\n"); 1178 // 1179 // Check input parameters, generate warnings and errors. 1180 // 1181 if (PM_OKAY != AssertCodeOverscan(in, fitSpec, fit, overscan, stat, nBin, bias, dark)) { 1182 return(in); 1183 } 1184 // 1185 // Determine trimmed image from metadata. 1186 // 1187 psImage *trimmedImg = p_psDetermineTrimmedImage(in); 1188 1189 // 1190 // Subtract overscan frames if necessary. 1191 // 1192 if (overscan == true) { 1193 pmOverscanAxis overScanAxis = GetOverscanAxis(in); 1194 // 1195 // Create a psStats data structure and determine the highest 1196 // priority stats option. 1197 // 1198 psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN); 1199 if (stat != NULL) { 1200 myStats->options = GenNewStatOptions(stat); 1201 } 1202 1203 // 1204 // Reduce overscan images to a single pixel, then subtract. 1205 // This code is no longer required as of SDRS 12-09. 1206 // 1207 if (overScanAxis == PM_OVERSCAN_ALL) { 1208 if (false == OverscanReducePixel(trimmedImg, in->bias, myStats)) { 418 PS_ASSERT_READOUT_NON_NULL(in, NULL); 419 PS_ASSERT_READOUT_NON_EMPTY(in, NULL); 420 PS_ASSERT_READOUT_TYPE(in, PS_TYPE_F32, NULL); 421 422 psImage *image = in->image; // The input image 423 424 // Overscan processing 425 if (overscanOpts) { 426 // Check for an unallowable pmFit. 427 if (overscanOpts->fitType != PM_FIT_NONE && overscanOpts->fitType != PM_FIT_POLY_ORD && 428 overscanOpts->fitType != PM_FIT_POLY_CHEBY && overscanOpts->fitType != PM_FIT_SPLINE) { 429 psError(PS_ERR_UNKNOWN, true, "Invalid fit type (%d). Returning original image.\n", overscanOpts->fitType); 430 return(in); 431 } 432 433 psList *overscans = in->bias; // List of the overscan images 434 435 psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN); // A new psStats, to avoid clobbering original 436 myStats->options = GenNewStatOptions(overscanOpts->stat); 437 438 // Reduce all overscan pixels to a single value 439 if (overscanOpts->single) { 440 psVector *pixels = psVectorAlloc(0, PS_TYPE_F32); 441 pixels->n = 0; 442 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 443 psImage *overscan = NULL; // Overscan image from iterator 444 while ((overscan = psListGetAndIncrement(iter))) { 445 int index = pixels->n; // Index 446 pixels = psVectorRealloc(pixels, pixels->n + overscan->numRows * overscan->numCols); 447 // XXX Reimplement with memcpy 448 for (int i = 0; i < overscan->numRows; i++) { 449 for (int j = 0; j < overscan->numCols; j++) { 450 pixels->data.F32[index++] = overscan->data.F32[i][j]; 451 } 452 } 453 454 } 455 psFree(iter); 456 457 (void)psVectorStats(myStats, pixels, NULL, NULL, 0); 458 double reduced = NAN; // Result of statistics 459 if (! p_psGetStatValue(myStats, &reduced)) { 460 psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation. Returning input image.\n"); 1209 461 return(in); 1210 462 } 1211 psFree(myStats);463 (void)psBinaryOp(image, image, "-", psScalarAlloc((float)reduced, PS_TYPE_F32)); 1212 464 } else { 1213 // 1214 // Reduce the overscan images to a single overscan vector. 1215 // 1216 psVector *overscanVector = OverscanReduce(in->image, overScanAxis, 1217 in->bias, fitSpec, 1218 fit, myStats); 1219 if (overscanVector == NULL) { 1220 psError(PS_ERR_UNKNOWN, false, "Could not reduce overscan images to a single overscan vector. Returning in image\n"); 1221 psFree(myStats); 1222 return(in); 465 466 // We do the regular overscan subtraction 467 468 bool readRows = psMetadataLookupBool(NULL, in->parent->concepts, "CELL.READDIR");// Read direction 469 470 if (readRows) { 471 // The read direction is rows 472 psArray *pixels = psArrayAlloc(image->numRows); // Array of vectors containing pixels 473 for (int i = 0; i < pixels->n; i++) { 474 psVector *values = psVectorAlloc(0, PS_TYPE_F32); 475 values->n = 0; 476 pixels->data[i] = values; 477 } 478 479 // Pull the pixels out into the vectors 480 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 481 psImage *overscan = NULL; // Overscan image from iterator 482 while ((overscan = psListGetAndIncrement(iter))) { 483 int diff = image->row0 - overscan->row0; // Offset between the two regions 484 for (int i = MAX(0,diff); i < MIN(image->numRows, overscan->numRows + diff); i++) { 485 // i is row on overscan 486 // XXX Reimplement with memcpy 487 psVector *values = pixels->data[i]; 488 int index = values->n; // Index in the vector 489 values = psVectorRealloc(values, values->n + overscan->numCols); 490 for (int j = 0; j < overscan->numCols; j++) { 491 values->data.F32[index++] = overscan->data.F32[i][j]; 492 } 493 values->n += overscan->numCols; 494 pixels->data[i] = values; // Update the pointer in case it's moved 495 } 496 } 497 psFree(iter); 498 499 // Reduce the overscans 500 psVector *reduced = overscanVector(overscanOpts, pixels, myStats); 501 psFree(pixels); 502 if (! reduced) { 503 return in; 504 } 505 506 // Subtract row by row 507 for (int i = 0; i < image->numRows; i++) { 508 for (int j = 0; j < image->numCols; j++) { 509 image->data.F32[i][j] -= reduced->data.F32[i]; 510 } 511 } 512 psFree(reduced); 513 514 } else { 515 // The read direction is columns 516 psArray *pixels = psArrayAlloc(image->numCols); // Array of vectors containing pixels 517 for (int i = 0; i < pixels->n; i++) { 518 psVector *values = psVectorAlloc(0, PS_TYPE_F32); 519 values->n = 0; 520 pixels->data[i] = values; 521 } 522 523 // Pull the pixels out into the vectors 524 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 525 psImage *overscan = NULL; // Overscan image from iterator 526 while ((overscan = psListGetAndIncrement(iter))) { 527 int diff = image->col0 - overscan->col0; // Offset between the two regions 528 for (int i = MAX(0,diff); i < MIN(image->numCols, overscan->numCols + diff); i++) { 529 // i is column on overscan 530 // XXX Reimplement with memcpy 531 psVector *values = pixels->data[i]; 532 int index = values->n; // Index in the vector 533 values = psVectorRealloc(values, values->n + overscan->numRows); 534 for (int j = 0; j < overscan->numRows; j++) { 535 values->data.F32[index++] = overscan->data.F32[i][j]; 536 } 537 values->n += overscan->numRows; 538 pixels->data[i] = values; // Update the pointer in case it's moved 539 } 540 } 541 psFree(iter); 542 543 // Reduce the overscans 544 psVector *reduced = overscanVector(overscanOpts, pixels, myStats); 545 psFree(pixels); 546 if (! reduced) { 547 return in; 548 } 549 550 // Subtract column by column 551 for (int i = 0; i < image->numCols; i++) { 552 for (int j = 0; j < image->numRows; j++) { 553 image->data.F32[j][i] -= reduced->data.F32[i]; 554 } 555 } 556 psFree(reduced); 1223 557 } 1224 1225 // 1226 // Rebin the overscan vector if necessary. 1227 // 1228 psS32 newBin = RebinOverscanVector(overscanVector, nBin, myStats); 1229 if (newBin < 0) { 1230 psError(PS_ERR_UNKNOWN, false, "Could rebin the overscan vector. Returning in image\n"); 1231 psFree(myStats); 1232 return(in); 1233 } 1234 1235 // 1236 // If necessary, fit a psPolynomial or psSpline to the overscan vector. 1237 // Then, unbin the overscan vector to appropriate length for the in image. 1238 // 1239 if ((fit == PM_FIT_POLYNOMIAL) || (fit == PM_FIT_SPLINE)) { 1240 overscanVector = FitOverscanVectorAndUnbin(trimmedImg, overscanVector, overScanAxis, fitSpec, fit, newBin); 1241 if (overscanVector == NULL) { 1242 psError(PS_ERR_UNKNOWN, false, "Could not fit the polynomial or spline to the overscan vector. Returning in image\n"); 1243 psFree(myStats); 1244 return(in); 1245 } 1246 } else { 1247 overscanVector = UnbinOverscanVector(trimmedImg, overscanVector, overScanAxis, newBin); 1248 } 1249 1250 // 1251 // Subtract the overscan vector from the input image. 1252 // 1253 SubtractVectorFromImage(trimmedImg, overscanVector, overScanAxis); 1254 psFree(myStats); 1255 psFree(overscanVector); 1256 } 1257 } 1258 1259 // 1260 // Perform bias subtraction if necessary. 1261 // 1262 if (bias != NULL) { 1263 if (PM_OKAY == AssertCodeBias(in, fitSpec, fit, overscan, stat, nBin, bias, dark)) { 1264 SubtractFrame(in, bias); 1265 } 1266 } 1267 1268 // 1269 // Perform dark subtraction if necessary. 1270 // 1271 if (dark != NULL) { 1272 if (PM_OKAY == AssertCodeDark(in, fitSpec, fit, overscan, stat, nBin, bias, dark)) { 1273 psBool rc; 1274 psF32 scale = 0.0; 1275 if (in->parent != NULL) { 1276 scale = psMetadataLookupS32(&rc, in->parent->concepts, "CELL.DARKTIME"); 1277 if (rc == false) { 1278 psLogMsg(__func__, PS_LOG_WARN, 1279 "WARNING: pmSubtractBias.(): could not determine CELL.FARKTIME from in->parent metadata.\n"); 1280 } 1281 } 1282 SubtractDarkFrame(in, dark, scale); 1283 } 1284 } 1285 1286 // 1287 // All done. 1288 // 1289 psTrace(".psModule.pmSubtracBias.pmSubtractBias", 4, 1290 "---- pmSubtractBias() exit ----\n"); 1291 return(in); 1292 } 1293 1294 558 } 559 psFree(myStats); 560 } // End of overscan subtraction 561 562 // Bias frame subtraction 563 if (bias) { 564 SubtractFrame(in, bias, 1.0); 565 } 566 567 if (dark) { 568 // Get the scaling 569 float inTime = psMetadataLookupF32(NULL, in->parent->concepts, "CELL.DARKTIME"); 570 float darkTime = psMetadataLookupF32(NULL, dark->parent->concepts, "CELL.DARKTIME"); 571 SubtractFrame(in, dark, inTime/darkTime); 572 } 573 574 return in; 575 } 576 577 -
trunk/psModules/src/imsubtract/pmSubtractBias.h
r6872 r6873 11 11 * @author GLG, MHPCC 12 12 * 13 * @version $Revision: 1. 5$ $Name: not supported by cvs2svn $14 * @date $Date: 2006-04-17 18: 01:05$13 * @version $Revision: 1.6 $ $Name: not supported by cvs2svn $ 14 * @date $Date: 2006-04-17 18:10:08 $ 15 15 * 16 16 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii -
trunk/psModules/src/imsubtract/pmSubtractSky.h
r6872 r6873 6 6 * @author GLG, MHPCC 7 7 * 8 * @version $Revision: 1. 2$ $Name: not supported by cvs2svn $9 * @date $Date: 2006-04-17 18: 01:05$8 * @version $Revision: 1.3 $ $Name: not supported by cvs2svn $ 9 * @date $Date: 2006-04-17 18:10:08 $ 10 10 * 11 11 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii -
trunk/psModules/src/objects/pmModelGroup.c
r6872 r6873 6 6 * @author EAM, IfA 7 7 * 8 * @version $Revision: 1. 3$ $Name: not supported by cvs2svn $9 * @date $Date: 2006-04-17 18: 01:05$8 * @version $Revision: 1.4 $ $Name: not supported by cvs2svn $ 9 * @date $Date: 2006-04-17 18:10:08 $ 10 10 * 11 11 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii -
trunk/psModules/src/objects/pmModelGroup.h
r6872 r6873 9 9 * @author EAM, IfA 10 10 * 11 * @version $Revision: 1. 3$ $Name: not supported by cvs2svn $12 * @date $Date: 2006-04-17 18: 01:05$11 * @version $Revision: 1.4 $ $Name: not supported by cvs2svn $ 12 * @date $Date: 2006-04-17 18:10:08 $ 13 13 * 14 14 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii -
trunk/psModules/src/objects/pmObjects.c
r6511 r6873 6 6 * @author EAM, IfA: significant modifications. 7 7 * 8 * @version $Revision: 1.1 0$ $Name: not supported by cvs2svn $9 * @date $Date: 2006-0 3-04 01:01:33$8 * @version $Revision: 1.11 $ $Name: not supported by cvs2svn $ 9 * @date $Date: 2006-04-17 18:10:08 $ 10 10 * 11 11 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii … … 19 19 #include "pmObjects.h" 20 20 #include "pmModelGroup.h" 21 /******************************************************************************22 pmPeakAlloc(): Allocate the pmPeak data structure and set appropriate members.23 *****************************************************************************/24 pmPeak *pmPeakAlloc(psS32 x,25 psS32 y,26 psF32 counts,27 pmPeakType class)28 {29 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);30 pmPeak *tmp = (pmPeak *) psAlloc(sizeof(pmPeak));31 tmp->x = x;32 tmp->y = y;33 tmp->counts = counts;34 tmp->class = class;35 21 36 psTrace(__func__, 3, "---- %s() end ----\n", __func__);37 return(tmp);38 }39 40 /******************************************************************************41 pmMomentsAlloc(): Allocate the pmMoments structure and initialize the members42 to zero.43 *****************************************************************************/44 pmMoments *pmMomentsAlloc()45 {46 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);47 pmMoments *tmp = (pmMoments *) psAlloc(sizeof(pmMoments));48 tmp->x = 0.0;49 tmp->y = 0.0;50 tmp->Sx = 0.0;51 tmp->Sy = 0.0;52 tmp->Sxy = 0.0;53 tmp->Sum = 0.0;54 tmp->Peak = 0.0;55 tmp->Sky = 0.0;56 tmp->nPixels = 0;57 58 psTrace(__func__, 3, "---- %s() end ----\n", __func__);59 return(tmp);60 }61 62 static void modelFree(pmModel *tmp)63 {64 psTrace(__func__, 4, "---- %s() begin ----\n", __func__);65 psFree(tmp->params);66 psFree(tmp->dparams);67 psTrace(__func__, 4, "---- %s() end ----\n", __func__);68 }69 70 static void sourceFree(pmSource *tmp)71 {72 psTrace(__func__, 4, "---- %s() begin ----\n", __func__);73 psFree(tmp->peak);74 psFree(tmp->pixels);75 psFree(tmp->weight);76 psFree(tmp->mask);77 psFree(tmp->moments);78 psFree(tmp->modelPSF);79 psFree(tmp->modelFLT);80 psTrace(__func__, 4, "---- %s() end ----\n", __func__);81 }82 83 /******************************************************************************84 getRowVectorFromImage(): a private function which simply returns a85 psVector containing the specified row of data from the psImage.86 87 XXX: Is there a better way to do this?88 XXX EAM: does this really need to alloc a new vector???89 *****************************************************************************/90 static psVector *getRowVectorFromImage(psImage *image,91 psU32 row)92 {93 psTrace(__func__, 4, "---- %s() begin ----\n", __func__);94 PS_ASSERT_IMAGE_NON_NULL(image, NULL);95 PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);96 97 psVector *tmpVector = psVectorAlloc(image->numCols, PS_TYPE_F32);98 tmpVector->n = tmpVector->nalloc;99 for (psU32 col = 0; col < image->numCols ; col++) {100 tmpVector->data.F32[col] = image->data.F32[row][col];101 }102 psTrace(__func__, 4, "---- %s() end ----\n", __func__);103 return(tmpVector);104 }105 106 /******************************************************************************107 myListAddPeak(): A private function which allocates a psArray, if the list108 argument is NULL, otherwise it adds the peak to that list.109 XXX EAM : changed the output to psArray110 XXX EAM : Switched row, col args111 XXX EAM : NOTE: this was changed in the call, so the new code is consistent112 *****************************************************************************/113 static psArray *myListAddPeak(psArray *list,114 psS32 row,115 psS32 col,116 psF32 counts,117 pmPeakType type)118 {119 psTrace(__func__, 4, "---- %s() begin ----\n", __func__);120 pmPeak *tmpPeak = pmPeakAlloc(col, row, counts, type);121 122 if (list == NULL) {123 list = psArrayAlloc(100);124 list->n = 0;125 }126 psArrayAdd(list, 100, tmpPeak);127 psFree (tmpPeak);128 // XXX EAM : is this free appropriate? (does psArrayAdd increment memory counter?)129 130 psTrace(__func__, 4, "---- %s() end ----\n", __func__);131 return(list);132 }133 134 135 /******************************************************************************136 bool checkRadius2(): private function which simply determines if the (x, y)137 point is within the radius of the specified peak.138 139 XXX: macro this for performance.140 XXX: this is rather inefficient - at least compute and compare against radius^2141 *****************************************************************************/142 static bool checkRadius2(psF32 xCenter,143 psF32 yCenter,144 psF32 radius,145 psF32 x,146 psF32 y)147 {148 psTrace(__func__, 4, "---- %s() begin ----\n", __func__);149 /// XXX EAM should compare with hypot (x,y) for speed150 if ((PS_SQR(x - xCenter) + PS_SQR(y - yCenter)) < PS_SQR(radius)) {151 return(true);152 }153 154 psTrace(__func__, 4, "---- %s(false) end ----\n", __func__);155 return(false);156 }157 158 // XXX: Macro this.159 static bool isItInThisRegion(const psRegion valid,160 psS32 x,161 psS32 y)162 {163 psTrace(__func__, 4, "---- %s() begin ----\n", __func__);164 if ((x >= valid.x0) &&165 (x <= valid.x1) &&166 (y >= valid.y0) &&167 (y <= valid.y1)) {168 psTrace(__func__, 4, "---- %s(true) end ----\n", __func__);169 return(true);170 }171 psTrace(__func__, 4, "---- %s(false) end ----\n", __func__);172 return(false);173 }174 175 /******************************************************************************176 findValue(source, level, row, col, dir): a private function which determines177 the column coordinate of the model function which has the value "level". If178 dir equals 0, then you loop leftwards from the peak pixel, otherwise,179 rightwards.180 181 XXX: reverse order of row,col args?182 183 XXX: Input row/col are in image coords.184 185 XXX: The result is returned in image coords.186 *****************************************************************************/187 static psF32 findValue(pmSource *source,188 psF32 level,189 psU32 row,190 psU32 col,191 psU32 dir)192 {193 psTrace(__func__, 4, "---- %s() begin ----\n", __func__);194 //195 // Convert coords to subImage space.196 //197 psU32 subRow = row - source->pixels->row0;198 psU32 subCol = col - source->pixels->col0;199 200 // Ensure that the starting column is allowable.201 if (!((0 <= subCol) && (subCol < source->pixels->numCols))) {202 psError(PS_ERR_UNKNOWN, true, "Starting column outside subImage range");203 psTrace(__func__, 4, "---- %s(NAN) end ----\n", __func__);204 return(NAN);205 }206 if (!((0 <= subRow) && (subRow < source->pixels->numRows))) {207 psTrace(__func__, 4, "---- %s(NAN) end ----\n", __func__);208 psError(PS_ERR_UNKNOWN, true, "Starting row outside subImage range");209 return(NAN);210 }211 212 // XXX EAM : i changed this to match pmModelEval above, but see213 // XXX EAM the note below in pmSourceContour214 psF32 oldValue = pmModelEval(source->modelFLT, source->pixels, subCol, subRow);215 if (oldValue == level) {216 psTrace(__func__, 4, "---- %s() end ----\n", __func__);217 return(((psF32) (subCol + source->pixels->col0)));218 }219 220 //221 // We define variables incr and lastColumn so that we can use the same loop222 // whether we are stepping leftwards, or rightwards.223 //224 psS32 incr;225 psS32 lastColumn;226 if (dir == 0) {227 incr = -1;228 lastColumn = -1;229 } else {230 incr = 1;231 lastColumn = source->pixels->numCols;232 }233 subCol+=incr;234 235 while (subCol != lastColumn) {236 psF32 newValue = pmModelEval(source->modelFLT, source->pixels, subCol, subRow);237 if (oldValue == level) {238 psTrace(__func__, 4, "---- %s() end ----\n", __func__);239 return((psF32) (subCol + source->pixels->col0));240 }241 242 if ((newValue <= level) && (level <= oldValue)) {243 // This is simple linear interpolation.244 psTrace(__func__, 4, "---- %s() end ----\n", __func__);245 return( ((psF32) (subCol + source->pixels->col0)) + ((psF32) incr) * ((level - newValue) / (oldValue - newValue)) );246 }247 248 if ((oldValue <= level) && (level <= newValue)) {249 // This is simple linear interpolation.250 psTrace(__func__, 4, "---- %s() end ----\n", __func__);251 return( ((psF32) (subCol + source->pixels->col0)) + ((psF32) incr) * ((level - oldValue) / (newValue - oldValue)) );252 }253 254 subCol+=incr;255 }256 257 psTrace(__func__, 4, "---- %s(NAN) end ----\n", __func__);258 return(NAN);259 }260 261 /******************************************************************************262 pmModelAlloc(): Allocate the pmModel structure, along with its parameters,263 and initialize the type member. Initialize the params to 0.0.264 XXX EAM: simplifying code with pmModelParameterCount265 *****************************************************************************/266 pmModel *pmModelAlloc(pmModelType type)267 {268 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);269 pmModel *tmp = (pmModel *) psAlloc(sizeof(pmModel));270 271 tmp->type = type;272 tmp->chisq = 0.0;273 tmp->nIter = 0;274 tmp->radius = 0;275 tmp->status = PM_MODEL_UNTRIED;276 277 psS32 Nparams = pmModelParameterCount(type);278 if (Nparams == 0) {279 psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");280 return(NULL);281 }282 283 tmp->params = psVectorAlloc(Nparams, PS_TYPE_F32);284 tmp->dparams = psVectorAlloc(Nparams, PS_TYPE_F32);285 tmp->params->n = tmp->params->nalloc;286 tmp->dparams->n = tmp->dparams->nalloc;287 288 for (psS32 i = 0; i < tmp->params->n; i++) {289 tmp->params->data.F32[i] = 0.0;290 tmp->dparams->data.F32[i] = 0.0;291 }292 293 psMemSetDeallocator(tmp, (psFreeFunc) modelFree);294 psTrace(__func__, 3, "---- %s() end ----\n", __func__);295 return(tmp);296 }297 298 /******************************************************************************299 XXX EAM : we can now free these pixels - memory ref is incremented now300 *****************************************************************************/301 302 /******************************************************************************303 pmSourceAlloc(): Allocate the pmSource structure and initialize its members304 to NULL.305 *****************************************************************************/306 pmSource *pmSourceAlloc()307 {308 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);309 pmSource *tmp = (pmSource *) psAlloc(sizeof(pmSource));310 tmp->peak = NULL;311 tmp->pixels = NULL;312 tmp->weight = NULL;313 tmp->mask = NULL;314 tmp->moments = NULL;315 tmp->modelPSF = NULL;316 tmp->modelFLT = NULL;317 tmp->type = 0;318 psMemSetDeallocator(tmp, (psFreeFunc) sourceFree);319 320 psTrace(__func__, 3, "---- %s() end ----\n", __func__);321 return(tmp);322 }323 324 /******************************************************************************325 pmFindVectorPeaks(vector, threshold): Find all local peaks in the given vector326 above the given threshold. Returns a vector of type PS_TYPE_U32 containing327 the location (x value) of all peaks.328 329 XXX: What types should be supported? Only F32 is implemented.330 331 XXX: We currently step through the input vector twice; once to determine the332 size of the output vector, then to set the values of the output vector.333 Depending upon actual use, this may need to be optimized.334 *****************************************************************************/335 psVector *pmFindVectorPeaks(const psVector *vector,336 psF32 threshold)337 {338 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);339 PS_ASSERT_VECTOR_NON_NULL(vector, NULL);340 PS_ASSERT_VECTOR_NON_EMPTY(vector, NULL);341 PS_ASSERT_VECTOR_TYPE(vector, PS_TYPE_F32, NULL);342 int count = 0;343 int n = vector->n;344 345 //346 // Special case: the input vector has a single element.347 //348 if (n == 1) {349 psVector *tmpVector = NULL;350 ;351 if (vector->data.F32[0] > threshold) {352 tmpVector = psVectorAlloc(1, PS_TYPE_U32);353 tmpVector->n = 1;354 tmpVector->data.U32[0] = 0;355 } else {356 tmpVector = psVectorAlloc(0, PS_TYPE_U32);357 }358 psTrace(__func__, 3, "---- %s() end ----\n", __func__);359 return(tmpVector);360 }361 362 //363 // Determine if first pixel is a peak364 //365 if ((vector->data.F32[0] > vector->data.F32[1]) &&366 (vector->data.F32[0] > threshold)) {367 count++;368 }369 370 //371 // Determine if interior pixels are peaks372 //373 for (psU32 i = 1; i < n-1 ; i++) {374 if ((vector->data.F32[i] > vector->data.F32[i-1]) &&375 (vector->data.F32[i] > vector->data.F32[i+1]) &&376 (vector->data.F32[i] > threshold)) {377 count++;378 }379 }380 381 //382 // Determine if last pixel is a peak383 //384 if ((vector->data.F32[n-1] > vector->data.F32[n-2]) &&385 (vector->data.F32[n-1] > threshold)) {386 count++;387 }388 389 //390 // We know how many peaks exist, so we now allocate a psVector to store391 // those peaks.392 //393 psVector *tmpVector = psVectorAlloc(count, PS_TYPE_U32);394 tmpVector->n = tmpVector->nalloc;395 count = 0;396 397 //398 // Determine if first pixel is a peak399 //400 if ((vector->data.F32[0] > vector->data.F32[1]) &&401 (vector->data.F32[0] > threshold)) {402 tmpVector->data.U32[count++] = 0;403 }404 405 //406 // Determine if interior pixels are peaks407 //408 for (psU32 i = 1; i < (n-1) ; i++) {409 if ((vector->data.F32[i] > vector->data.F32[i-1]) &&410 (vector->data.F32[i] > vector->data.F32[i+1]) &&411 (vector->data.F32[i] > threshold)) {412 tmpVector->data.U32[count++] = i;413 }414 }415 416 //417 // Determine if last pixel is a peak418 //419 if ((vector->data.F32[n-1] > vector->data.F32[n-2]) &&420 (vector->data.F32[n-1] > threshold)) {421 tmpVector->data.U32[count++] = n-1;422 }423 424 psTrace(__func__, 3, "---- %s() end ----\n", __func__);425 return(tmpVector);426 }427 428 429 /******************************************************************************430 pmFindImagePeaks(image, threshold): Find all local peaks in the given psImage431 above the given threshold. Returns a psArray containing location (x/y value)432 of all peaks.433 434 XXX: I'm not convinced the peak type definition in the SDRS is mutually435 exclusive. Some peaks can have multiple types. Edges for sure. Also, a436 digonal line with the same value at each point will have a peak for every437 point on that line.438 439 XXX: This does not work if image has either a single row, or a single column.440 441 XXX: In the output psArray elements, should we use the image row/column offsets?442 Currently, we do not.443 444 XXX: Merge with CVS 1.20. This had the proper code for images with a single445 row or column.446 *****************************************************************************/447 psArray *pmFindImagePeaks(const psImage *image,448 psF32 threshold)449 {450 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);451 PS_ASSERT_IMAGE_NON_NULL(image, NULL);452 PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);453 if ((image->numRows == 1) || (image->numCols == 1)) {454 psError(PS_ERR_UNKNOWN, true, "Currently, input image must have at least 2 rows and 2 columns.");455 psTrace(__func__, 3, "---- %s(NULL) end ----\n", __func__);456 return(NULL);457 }458 psVector *tmpRow = NULL;459 psU32 col = 0;460 psU32 row = 0;461 psArray *list = NULL;462 463 //464 // Find peaks in row 0 only.465 //466 row = 0;467 tmpRow = getRowVectorFromImage((psImage *) image, row);468 psVector *row1 = pmFindVectorPeaks(tmpRow, threshold);469 470 for (psU32 i = 0 ; i < row1->n ; i++ ) {471 col = row1->data.U32[i];472 //473 // Determine if pixel (0,0) is a peak.474 //475 if (col == 0) {476 if ( (image->data.F32[row][col] > image->data.F32[row][col+1]) &&477 (image->data.F32[row][col] > image->data.F32[row+1][col]) &&478 (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {479 480 if (image->data.F32[row][col] > threshold) {481 list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);482 }483 }484 } else if (col < (image->numCols - 1)) {485 if ( (image->data.F32[row][col] >= image->data.F32[row][col-1]) &&486 (image->data.F32[row][col] > image->data.F32[row][col+1]) &&487 (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&488 (image->data.F32[row][col] > image->data.F32[row+1][col]) &&489 (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {490 if (image->data.F32[row][col] > threshold) {491 list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);492 }493 }494 495 } else if (col == (image->numCols - 1)) {496 if ( (image->data.F32[row][col] >= image->data.F32[row][col-1]) &&497 (image->data.F32[row][col] > image->data.F32[row+1][col]) &&498 (image->data.F32[row][col] >= image->data.F32[row+1][col-1])) {499 if (image->data.F32[row][col] > threshold) {500 list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);501 }502 }503 504 } else {505 psError(PS_ERR_UNKNOWN, true, "peak specified valid column range.");506 }507 }508 psFree (tmpRow);509 psFree (row1);510 511 //512 // Exit if this image has a single row.513 //514 if (image->numRows == 1) {515 psTrace(__func__, 3, "---- %s() end ----\n", __func__);516 return(list);517 }518 519 //520 // Find peaks in interior rows only.521 //522 for (row = 1 ; row < (image->numRows - 1) ; row++) {523 tmpRow = getRowVectorFromImage((psImage *) image, row);524 row1 = pmFindVectorPeaks(tmpRow, threshold);525 526 // Step through all local peaks in this row.527 for (psU32 i = 0 ; i < row1->n ; i++ ) {528 pmPeakType myType = PM_PEAK_UNDEF;529 col = row1->data.U32[i];530 531 if (col == 0) {532 // If col==0, then we can not read col-1 pixels533 if ((image->data.F32[row][col] > image->data.F32[row-1][col]) &&534 (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&535 (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&536 (image->data.F32[row][col] >= image->data.F32[row+1][col]) &&537 (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {538 myType = PM_PEAK_EDGE;539 list = myListAddPeak(list, row, col, image->data.F32[row][col], myType);540 }541 } else if (col < (image->numCols - 1)) {542 // This is an interior pixel543 if ((image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&544 (image->data.F32[row][col] > image->data.F32[row-1][col]) &&545 (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&546 (image->data.F32[row][col] > image->data.F32[row][col-1]) &&547 (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&548 (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&549 (image->data.F32[row][col] >= image->data.F32[row+1][col]) &&550 (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {551 if (image->data.F32[row][col] > threshold) {552 if ((image->data.F32[row][col] > image->data.F32[row-1][col-1]) &&553 (image->data.F32[row][col] > image->data.F32[row-1][col]) &&554 (image->data.F32[row][col] > image->data.F32[row-1][col+1]) &&555 (image->data.F32[row][col] > image->data.F32[row][col-1]) &&556 (image->data.F32[row][col] > image->data.F32[row][col+1]) &&557 (image->data.F32[row][col] > image->data.F32[row+1][col-1]) &&558 (image->data.F32[row][col] > image->data.F32[row+1][col]) &&559 (image->data.F32[row][col] > image->data.F32[row+1][col+1])) {560 myType = PM_PEAK_LONE;561 }562 563 if ((image->data.F32[row][col] == image->data.F32[row-1][col-1]) ||564 (image->data.F32[row][col] == image->data.F32[row-1][col]) ||565 (image->data.F32[row][col] == image->data.F32[row-1][col+1]) ||566 (image->data.F32[row][col] == image->data.F32[row][col-1]) ||567 (image->data.F32[row][col] == image->data.F32[row][col+1]) ||568 (image->data.F32[row][col] == image->data.F32[row+1][col-1]) ||569 (image->data.F32[row][col] == image->data.F32[row+1][col]) ||570 (image->data.F32[row][col] == image->data.F32[row+1][col+1])) {571 myType = PM_PEAK_FLAT;572 }573 574 list = myListAddPeak(list, row, col, image->data.F32[row][col], myType);575 }576 }577 } else if (col == (image->numCols - 1)) {578 // If col==numCols - 1, then we can not read col+1 pixels579 if ((image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&580 (image->data.F32[row][col] > image->data.F32[row-1][col]) &&581 (image->data.F32[row][col] > image->data.F32[row][col-1]) &&582 (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&583 (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&584 (image->data.F32[row][col] >= image->data.F32[row+1][col])) {585 myType = PM_PEAK_EDGE;586 list = myListAddPeak(list, row, col, image->data.F32[row][col], myType);587 }588 } else {589 psError(PS_ERR_UNKNOWN, true, "peak specified outside valid column range.");590 }591 592 }593 psFree (tmpRow);594 psFree (row1);595 }596 597 //598 // Find peaks in the last row only.599 //600 row = image->numRows - 1;601 tmpRow = getRowVectorFromImage((psImage *) image, row);602 row1 = pmFindVectorPeaks(tmpRow, threshold);603 for (psU32 i = 0 ; i < row1->n ; i++ ) {604 col = row1->data.U32[i];605 if (col == 0) {606 if ( (image->data.F32[row][col] > image->data.F32[row-1][col]) &&607 (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&608 (image->data.F32[row][col] > image->data.F32[row][col+1])) {609 if (image->data.F32[row][col] > threshold) {610 list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);611 }612 }613 } else if (col < (image->numCols - 1)) {614 if ( (image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&615 (image->data.F32[row][col] > image->data.F32[row-1][col]) &&616 (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&617 (image->data.F32[row][col] > image->data.F32[row][col-1]) &&618 (image->data.F32[row][col] >= image->data.F32[row][col+1])) {619 if (image->data.F32[row][col] > threshold) {620 list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);621 }622 }623 624 } else if (col == (image->numCols - 1)) {625 if ( (image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&626 (image->data.F32[row][col] > image->data.F32[row-1][col]) &&627 (image->data.F32[row][col] > image->data.F32[row][col-1])) {628 if (image->data.F32[row][col] > threshold) {629 list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);630 }631 }632 } else {633 psError(PS_ERR_UNKNOWN, true, "peak specified outside valid column range.");634 }635 }636 psFree (tmpRow);637 psFree (row1);638 psTrace(__func__, 3, "---- %s() end ----\n", __func__);639 return(list);640 }641 642 643 /******************************************************************************644 psCullPeaks(peaks, maxValue, valid): eliminate peaks from the psArray that have645 a peak value above the given maximum, or fall outside the valid region.646 647 XXX: Should the sky value be used when comparing the maximum?648 649 XXX: warning message if valid is NULL?650 651 XXX: changed API to create a NEW output psArray (should change name as well)652 653 XXX: Do we free the psList elements of those culled peaks?654 655 XXX EAM : do we still need pmCullPeaks, or only pmPeaksSubset?656 *****************************************************************************/657 psList *pmCullPeaks(psList *peaks,658 psF32 maxValue,659 const psRegion valid)660 {661 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);662 PS_ASSERT_PTR_NON_NULL(peaks, NULL);663 664 psListElem *tmpListElem = (psListElem *) peaks->head;665 psS32 indexNum = 0;666 667 // printf("pmCullPeaks(): list size is %d\n", peaks->size);668 while (tmpListElem != NULL) {669 pmPeak *tmpPeak = (pmPeak *) tmpListElem->data;670 if ((tmpPeak->counts > maxValue) ||671 (true == isItInThisRegion(valid, tmpPeak->x, tmpPeak->y))) {672 psListRemoveData(peaks, (psPtr) tmpPeak);673 }674 675 indexNum++;676 tmpListElem = tmpListElem->next;677 }678 679 psTrace(__func__, 3, "---- %s() end ----\n", __func__);680 return(peaks);681 }682 683 // XXX EAM: I changed this to return a new, subset array684 // rather than alter the existing one685 // XXX: Fix the *valid pointer.686 psArray *pmPeaksSubset(687 psArray *peaks,688 psF32 maxValue,689 const psRegion valid)690 {691 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);692 PS_ASSERT_PTR_NON_NULL(peaks, NULL);693 694 psArray *output = psArrayAlloc (200);695 output->n = 0;696 697 psTrace (".pmObjects.pmCullPeaks", 3, "list size is %d\n", peaks->n);698 699 for (int i = 0; i < peaks->n; i++) {700 pmPeak *tmpPeak = (pmPeak *) peaks->data[i];701 if (tmpPeak->counts > maxValue)702 continue;703 if (isItInThisRegion(valid, tmpPeak->x, tmpPeak->y))704 continue;705 psArrayAdd (output, 200, tmpPeak);706 }707 psTrace(__func__, 3, "---- %s() end ----\n", __func__);708 return(output);709 }710 711 /******************************************************************************712 pmSource *pmSourceLocalSky(image, peak, innerRadius, outerRadius): this713 routine creates a new pmSource data structure and sets the following members:714 ->pmPeak715 ->pmMoments->sky716 717 The sky value is set from the pixels in the square annulus surrounding the718 peak pixel.719 720 We simply create a subSet image and mask the inner pixels, then call721 psImageStats on that subImage+mask.722 723 XXX: The subImage has width of 1+2*outerRadius. Verify with IfA.724 725 XXX: Use static data structures for:726 subImage727 subImageMask728 myStats729 730 XXX: ensure that the inner and out radius fit in the actual image. Should731 we generate an error, or warning? Currently an error.732 733 XXX: Sync with IfA on whether the peak x/y coords are data structure coords,734 or they use the image row/column offsets.735 736 XXX: Should we simply set pmSource->peak = peak? If so, should we increase737 the reference counter? Or, should we copy the data structure?738 739 XXX: Currently the subimage always has an even number of rows/columns. Is740 this correct? Since there is a center pixel, maybe it should have an741 odd number of rows/columns.742 743 XXX: Use psTrace() for the print statements.744 745 XXX: Don't use separate structs for the subimage and mask. Use the source->746 members.747 *****************************************************************************/748 749 bool pmSourceLocalSky(750 pmSource *source,751 psStatsOptions statsOptions,752 psF32 Radius)753 {754 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);755 PS_ASSERT_PTR_NON_NULL(source, false);756 PS_ASSERT_IMAGE_NON_NULL(source->pixels, false);757 PS_ASSERT_IMAGE_NON_NULL(source->mask, false);758 PS_ASSERT_PTR_NON_NULL(source->peak, false);759 PS_ASSERT_INT_POSITIVE(Radius, false);760 PS_ASSERT_INT_NONNEGATIVE(Radius, false);761 762 psImage *image = source->pixels;763 psImage *mask = source->mask;764 pmPeak *peak = source->peak;765 psRegion srcRegion;766 767 srcRegion = psRegionForSquare(peak->x, peak->y, Radius);768 srcRegion = psRegionForImage(mask, srcRegion);769 770 psImageMaskRegion(mask, srcRegion, "OR", PSPHOT_MASK_MARKED);771 psStats *myStats = psStatsAlloc(statsOptions);772 myStats = psImageStats(myStats, image, mask, 0xff);773 psImageMaskRegion(mask, srcRegion, "AND", ~PSPHOT_MASK_MARKED);774 775 psF64 tmpF64;776 p_psGetStatValue(myStats, &tmpF64);777 psFree(myStats);778 779 if (isnan(tmpF64)) {780 psTrace(__func__, 3, "---- %s(false) end ----\n", __func__);781 return(false);782 }783 source->moments = pmMomentsAlloc();784 source->moments->Sky = (psF32) tmpF64;785 psTrace(__func__, 3, "---- %s(true) end ----\n", __func__);786 return (true);787 }788 789 /******************************************************************************790 pmSourceMoments(source, radius): this function takes a subImage defined in the791 pmSource data structure, along with the peak location, and determines the792 various moments associated with that peak.793 794 Requires the following to have been created:795 pmSource796 pmSource->peak797 pmSource->pixels798 pmSource->weight799 pmSource->mask800 801 XXX: The peak calculations are done in image coords, not subImage coords.802 803 XXX EAM : this version clips input pixels on S/N804 XXX EAM : this version returns false for several reasons805 *****************************************************************************/806 # define VALID_RADIUS(X,Y,RAD2) (((RAD2) >= (PS_SQR(X) + PS_SQR(Y))) ? 1 : 0)807 808 bool pmSourceMoments(pmSource *source,809 psF32 radius)810 {811 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);812 PS_ASSERT_PTR_NON_NULL(source, false);813 PS_ASSERT_PTR_NON_NULL(source->peak, false);814 PS_ASSERT_PTR_NON_NULL(source->pixels, false);815 PS_ASSERT_PTR_NON_NULL(source->mask, false);816 PS_ASSERT_FLOAT_LARGER_THAN(radius, 0.0, false);817 818 //819 // XXX: Verify the setting for sky if source->moments == NULL.820 //821 psF32 sky = 0.0;822 if (source->moments == NULL) {823 source->moments = pmMomentsAlloc();824 } else {825 sky = source->moments->Sky;826 }827 828 //829 // Sum = SUM (z - sky)830 // X1 = SUM (x - xc)*(z - sky)831 // X2 = SUM (x - xc)^2 * (z - sky)832 // XY = SUM (x - xc)*(y - yc)*(z - sky)833 //834 psF32 peakPixel = -PS_MAX_F32;835 psS32 numPixels = 0;836 psF32 Sum = 0.0;837 psF32 X1 = 0.0;838 psF32 Y1 = 0.0;839 psF32 X2 = 0.0;840 psF32 Y2 = 0.0;841 psF32 XY = 0.0;842 psF32 x = 0;843 psF32 y = 0;844 psF32 R2 = PS_SQR(radius);845 846 psF32 xPeak = source->peak->x;847 psF32 yPeak = source->peak->y;848 849 // XXX why do I get different results for these two methods of finding Sx?850 // XXX Sx, Sy would be better measured if we clip pixels close to sky851 // XXX Sx, Sy can still be imaginary, so we probably need to keep Sx^2?852 // We loop through all pixels in this subimage (source->pixels), and for each853 // pixel that is not masked, AND within the radius of the peak pixel, we854 // proceed with the moments calculation. need to do two loops for a855 // numerically stable result. first loop: get the sums.856 // XXX EAM : mask == 0 is valid857 858 for (psS32 row = 0; row < source->pixels->numRows ; row++) {859 for (psS32 col = 0; col < source->pixels->numCols ; col++) {860 if ((source->mask != NULL) && (source->mask->data.U8[row][col])) {861 continue;862 }863 864 psF32 xDiff = col + source->pixels->col0 - xPeak;865 psF32 yDiff = row + source->pixels->row0 - yPeak;866 867 // XXX EAM : calculate xDiff, yDiff up front;868 // radius is just a function of (xDiff, yDiff)869 if (!VALID_RADIUS(xDiff, yDiff, R2)) {870 continue;871 }872 873 psF32 pDiff = source->pixels->data.F32[row][col] - sky;874 875 // XXX EAM : check for valid S/N in pixel876 // XXX EAM : should this limit be user-defined?877 if (pDiff / sqrt(source->weight->data.F32[row][col]) < 1) {878 continue;879 }880 881 Sum += pDiff;882 X1 += xDiff * pDiff;883 Y1 += yDiff * pDiff;884 XY += xDiff * yDiff * pDiff;885 886 X2 += PS_SQR(xDiff) * pDiff;887 Y2 += PS_SQR(yDiff) * pDiff;888 889 peakPixel = PS_MAX (source->pixels->data.F32[row][col], peakPixel);890 numPixels++;891 }892 }893 // XXX EAM - the limit is a bit arbitrary. make it user defined?894 if ((numPixels < 3) || (Sum <= 0)) {895 psTrace (".psModules.pmSourceMoments", 5, "no valid pixels for source\n");896 psTrace(__func__, 3, "---- %s(false) end ----\n", __func__);897 return (false);898 }899 900 psTrace (".psModules.pmSourceMoments", 5,901 "sky: %f Sum: %f X1: %f Y1: %f X2: %f Y2: %f XY: %f Npix: %d\n",902 sky, Sum, X1, Y1, X2, Y2, XY, numPixels);903 904 //905 // first moment X = X1/Sum + xc906 // second moment X = sqrt (X2/Sum - (X1/Sum)^2)907 // Sxy = XY / Sum908 //909 x = X1/Sum;910 y = Y1/Sum;911 if ((fabs(x) > radius) || (fabs(y) > radius)) {912 psTrace (".psModules.pmSourceMoments", 5,913 "large centroid swing; invalid peak %d, %d\n",914 source->peak->x, source->peak->y);915 psTrace(__func__, 3, "---- %s(false) end ----\n", __func__);916 return (false);917 }918 919 source->moments->x = x + xPeak;920 source->moments->y = y + yPeak;921 922 // XXX EAM : Sxy needs to have x*y subtracted923 source->moments->Sxy = XY/Sum - x*y;924 source->moments->Sum = Sum;925 source->moments->Peak = peakPixel;926 source->moments->nPixels = numPixels;927 928 // XXX EAM : these values can be negative, so we need to limit the range929 source->moments->Sx = sqrt(PS_MAX(X2/Sum - PS_SQR(x), 0));930 source->moments->Sy = sqrt(PS_MAX(Y2/Sum - PS_SQR(y), 0));931 932 psTrace (".psModules.pmSourceMoments", 4,933 "sky: %f Sum: %f x: %f y: %f Sx: %f Sy: %f Sxy: %f\n",934 sky, Sum, source->moments->x, source->moments->y,935 source->moments->Sx, source->moments->Sy, source->moments->Sxy);936 937 psTrace(__func__, 3, "---- %s(true) end ----\n", __func__);938 return(true);939 }940 941 // XXX EAM : I used942 int pmComparePeakAscend (const void **a, const void **b)943 {944 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);945 pmPeak *A = *(pmPeak **)a;946 pmPeak *B = *(pmPeak **)b;947 948 psF32 diff;949 950 diff = A->counts - B->counts;951 if (diff < FLT_EPSILON) {952 psTrace(__func__, 3, "---- %s(-1) end ----\n", __func__);953 return (-1);954 } else if (diff > FLT_EPSILON) {955 psTrace(__func__, 3, "---- %s(+1) end ----\n", __func__);956 return (+1);957 }958 psTrace(__func__, 3, "---- %s(0) end ----\n", __func__);959 return (0);960 }961 962 int pmComparePeakDescend (const void **a, const void **b)963 {964 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);965 pmPeak *A = *(pmPeak **)a;966 pmPeak *B = *(pmPeak **)b;967 968 psF32 diff;969 970 diff = A->counts - B->counts;971 if (diff < FLT_EPSILON) {972 psTrace(__func__, 3, "---- %s(+1) end ----\n", __func__);973 return (+1);974 } else if (diff > FLT_EPSILON) {975 psTrace(__func__, 3, "---- %s(-1) end ----\n", __func__);976 return (-1);977 }978 psTrace(__func__, 3, "---- %s(0) end ----\n", __func__);979 return (0);980 }981 982 /******************************************************************************983 pmSourcePSFClump(source, metadata): Find the likely PSF clump in the984 sigma-x, sigma-y plane. return 0,0 clump in case of error.985 *****************************************************************************/986 987 // XXX EAM include a S/N cutoff in selecting the sources?988 pmPSFClump pmSourcePSFClump(psArray *sources, psMetadata *metadata)989 {990 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);991 992 # define NPIX 10993 # define SCALE 0.1994 995 psArray *peaks = NULL;996 pmPSFClump emptyClump = {0.0, 0.0, 0.0, 0.0};997 pmPSFClump psfClump = emptyClump;998 999 PS_ASSERT_PTR_NON_NULL(sources, emptyClump);1000 PS_ASSERT_PTR_NON_NULL(metadata, emptyClump);1001 1002 // find the sigmaX, sigmaY clump1003 {1004 psStats *stats = NULL;1005 psImage *splane = NULL;1006 int binX, binY;1007 bool status;1008 1009 psF32 SX_MAX = psMetadataLookupF32 (&status, metadata, "MOMENTS_SX_MAX");1010 if (!status)1011 SX_MAX = 10.0;1012 psF32 SY_MAX = psMetadataLookupF32 (&status, metadata, "MOMENTS_SY_MAX");1013 if (!status)1014 SY_MAX = 10.0;1015 1016 // construct a sigma-plane image1017 // psImageAlloc does zero the data1018 splane = psImageAlloc (SX_MAX/SCALE, SY_MAX/SCALE, PS_TYPE_F32);1019 for (int i = 0; i < splane->numRows; i++)1020 {1021 memset (splane->data.F32[i], 0, splane->numCols*sizeof(PS_TYPE_F32));1022 }1023 1024 // place the sources in the sigma-plane image (ignore 0,0 values?)1025 for (psS32 i = 0 ; i < sources->n ; i++)1026 {1027 pmSource *tmpSrc = (pmSource *) sources->data[i];1028 if (tmpSrc == NULL) {1029 continue;1030 }1031 if (tmpSrc->moments == NULL) {1032 continue;1033 }1034 1035 // Sx,Sy are limited at 0. a peak at 0,0 is artificial1036 if ((fabs(tmpSrc->moments->Sx) < FLT_EPSILON) && (fabs(tmpSrc->moments->Sy) < FLT_EPSILON)) {1037 continue;1038 }1039 1040 // for the moment, force splane dimensions to be 10x10 image pix1041 binX = tmpSrc->moments->Sx/SCALE;1042 if (binX < 0)1043 continue;1044 if (binX >= splane->numCols)1045 continue;1046 1047 binY = tmpSrc->moments->Sy/SCALE;1048 if (binY < 0)1049 continue;1050 if (binY >= splane->numRows)1051 continue;1052 1053 splane->data.F32[binY][binX] += 1.0;1054 }1055 1056 // find the peak in this image1057 stats = psStatsAlloc (PS_STAT_MAX);1058 stats = psImageStats (stats, splane, NULL, 0);1059 peaks = pmFindImagePeaks (splane, stats[0].max / 2);1060 psTrace (".pmObjects.pmSourceRoughClass", 2, "clump threshold is %f\n", stats[0].max/2);1061 1062 psFree (splane);1063 psFree (stats);1064 1065 }1066 // XXX EAM : possible errors:1067 // 1) no peak in splane1068 // 2) no significant peak in splane1069 1070 // measure statistics on Sx, Sy if Sx, Sy within range of clump1071 {1072 pmPeak *clump;1073 psF32 minSx, maxSx;1074 psF32 minSy, maxSy;1075 psVector *tmpSx = NULL;1076 psVector *tmpSy = NULL;1077 psStats *stats = NULL;1078 1079 // XXX EAM : this lets us takes the single highest peak1080 psArraySort (peaks, pmComparePeakDescend);1081 clump = peaks->data[0];1082 psTrace (".pmObjects.pmSourceRoughClass", 2, "clump is at %d, %d (%f)\n", clump->x, clump->y, clump->counts);1083 1084 // define section window for clump1085 minSx = clump->x * SCALE - 0.2;1086 maxSx = clump->x * SCALE + 0.2;1087 minSy = clump->y * SCALE - 0.2;1088 maxSy = clump->y * SCALE + 0.2;1089 1090 tmpSx = psVectorAlloc (sources->n, PS_TYPE_F32);1091 tmpSy = psVectorAlloc (sources->n, PS_TYPE_F32);1092 tmpSx->n = 0;1093 tmpSy->n = 0;1094 1095 // XXX clip sources based on flux?1096 // create vectors with Sx, Sy values in window1097 for (psS32 i = 0 ; i < sources->n ; i++)1098 {1099 pmSource *tmpSrc = (pmSource *) sources->data[i];1100 1101 if (tmpSrc->moments->Sx < minSx)1102 continue;1103 if (tmpSrc->moments->Sx > maxSx)1104 continue;1105 if (tmpSrc->moments->Sy < minSy)1106 continue;1107 if (tmpSrc->moments->Sy > maxSy)1108 continue;1109 tmpSx->data.F32[tmpSx->n] = tmpSrc->moments->Sx;1110 tmpSy->data.F32[tmpSy->n] = tmpSrc->moments->Sy;1111 tmpSx->n++;1112 tmpSy->n++;1113 if (tmpSx->n == tmpSx->nalloc) {1114 psVectorRealloc (tmpSx, tmpSx->nalloc + 100);1115 psVectorRealloc (tmpSy, tmpSy->nalloc + 100);1116 }1117 }1118 1119 // measures stats of Sx, Sy1120 stats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);1121 1122 stats = psVectorStats (stats, tmpSx, NULL, NULL, 0);1123 psfClump.X = stats->clippedMean;1124 psfClump.dX = stats->clippedStdev;1125 1126 stats = psVectorStats (stats, tmpSy, NULL, NULL, 0);1127 psfClump.Y = stats->clippedMean;1128 psfClump.dY = stats->clippedStdev;1129 1130 psTrace (".pmObjects.pmSourceRoughClass", 2, "clump X, Y: %f, %f\n", psfClump.X, psfClump.Y);1131 psTrace (".pmObjects.pmSourceRoughClass", 2, "clump DX, DY: %f, %f\n", psfClump.dX, psfClump.dY);1132 // these values should be pushed on the metadata somewhere1133 1134 psFree (stats);1135 psFree (peaks);1136 psFree (tmpSx);1137 psFree (tmpSy);1138 }1139 1140 psTrace(__func__, 3, "---- %s() end ----\n", __func__);1141 return (psfClump);1142 }1143 1144 /******************************************************************************1145 pmSourceRoughClass(source, metadata): make a guess at the source1146 classification.1147 1148 XXX: push the clump info into the metadata?1149 1150 XXX: How can this function ever return FALSE?1151 1152 XXX EAM : add the saturated mask value to metadata1153 *****************************************************************************/1154 1155 bool pmSourceRoughClass(psArray *sources, psMetadata *metadata, pmPSFClump clump)1156 {1157 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);1158 1159 psBool rc = true;1160 1161 int Nsat = 0;1162 int Ngal = 0;1163 int Nstar = 0;1164 int Npsf = 0;1165 int Ncr = 0;1166 int Nsatstar = 0;1167 psRegion allArray = psRegionSet (0, 0, 0, 0);1168 1169 psVector *starsn = psVectorAlloc (sources->n, PS_TYPE_F32);1170 starsn->n = 0;1171 1172 // check return status value (do these exist?)1173 bool status;1174 psF32 RDNOISE = psMetadataLookupF32 (&status, metadata, "RDNOISE");1175 psF32 GAIN = psMetadataLookupF32 (&status, metadata, "GAIN");1176 psF32 PSF_SN_LIM = psMetadataLookupF32 (&status, metadata, "PSF_SN_LIM");1177 // psF32 SATURATE = psMetadataLookupF32 (&status, metadata, "SATURATE");1178 1179 // XXX allow clump size to be scaled relative to sigmas?1180 // make rough IDs based on clumpX,Y,DX,DY1181 for (psS32 i = 0 ; i < sources->n ; i++) {1182 1183 pmSource *tmpSrc = (pmSource *) sources->data[i];1184 1185 tmpSrc->peak->class = 0;1186 1187 psF32 sigX = tmpSrc->moments->Sx;1188 psF32 sigY = tmpSrc->moments->Sy;1189 1190 // calculate and save signal-to-noise estimates1191 psF32 S = tmpSrc->moments->Sum;1192 psF32 A = 4 * M_PI * sigX * sigY;1193 psF32 B = tmpSrc->moments->Sky;1194 psF32 RT = sqrt(S + (A * B) + (A * PS_SQR(RDNOISE) / sqrt(GAIN)));1195 psF32 SN = (S * sqrt(GAIN) / RT);1196 tmpSrc->moments->SN = SN;1197 1198 // XXX EAM : can we use the value of SATURATE if mask is NULL?1199 int Nsatpix = psImageCountPixelMask (tmpSrc->mask, allArray, PSPHOT_MASK_SATURATED);1200 1201 // saturated star (size consistent with PSF or larger)1202 // Nsigma should be user-configured parameter1203 bool big = (sigX > (clump.X - clump.dX)) && (sigY > (clump.Y - clump.dY));1204 if ((Nsatpix > 1) && big) {1205 tmpSrc->type = PM_SOURCE_SATSTAR;1206 Nsatstar ++;1207 continue;1208 }1209 1210 // saturated object (not a star, eg bleed trails, hot pixels)1211 if (Nsatpix > 1) {1212 tmpSrc->type = PM_SOURCE_SATURATED;1213 Nsat ++;1214 continue;1215 }1216 1217 // likely defect (too small to be stellar) (push out to 3 sigma)1218 // low S/N objects which are small are probably stellar1219 // only set candidate defects if1220 if ((sigX < 0.05) || (sigY < 0.05)) {1221 tmpSrc->type = PM_SOURCE_DEFECT;1222 Ncr ++;1223 continue;1224 }1225 1226 // likely unsaturated galaxy (too large to be stellar)1227 if ((sigX > (clump.X + 3*clump.dX)) || (sigY > (clump.Y + 3*clump.dY))) {1228 tmpSrc->type = PM_SOURCE_GALAXY;1229 Ngal ++;1230 continue;1231 }1232 1233 // the rest are probable stellar objects1234 starsn->data.F32[starsn->n] = SN;1235 starsn->n ++;1236 Nstar ++;1237 1238 // PSF star (within 1.5 sigma of clump center1239 psF32 radius = hypot ((sigX-clump.X)/clump.dX, (sigY-clump.Y)/clump.dY);1240 if ((SN > PSF_SN_LIM) && (radius < 1.5)) {1241 tmpSrc->type = PM_SOURCE_PSFSTAR;1242 Npsf ++;1243 continue;1244 }1245 1246 // random type of star1247 tmpSrc->type = PM_SOURCE_OTHER;1248 }1249 1250 {1251 psStats *stats = NULL;1252 stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX);1253 stats = psVectorStats (stats, starsn, NULL, NULL, 0);1254 psLogMsg ("pmObjects", 3, "SN range: %f - %f\n", stats[0].min, stats[0].max);1255 psFree (stats);1256 psFree (starsn);1257 }1258 1259 psTrace (".pmObjects.pmSourceRoughClass", 2, "Nstar: %3d\n", Nstar);1260 psTrace (".pmObjects.pmSourceRoughClass", 2, "Npsf: %3d\n", Npsf);1261 psTrace (".pmObjects.pmSourceRoughClass", 2, "Ngal: %3d\n", Ngal);1262 psTrace (".pmObjects.pmSourceRoughClass", 2, "Nsatstar: %3d\n", Nsatstar);1263 psTrace (".pmObjects.pmSourceRoughClass", 2, "Nsat: %3d\n", Nsat);1264 psTrace (".pmObjects.pmSourceRoughClass", 2, "Ncr: %3d\n", Ncr);1265 1266 psTrace(__func__, 3, "---- %s(%d) end ----\n", __func__, rc);1267 return(rc);1268 }1269 1270 /** pmSourceDefinePixels()1271 *1272 * Define psImage subarrays for the source located at coordinates x,y on the1273 * image set defined by readout. The pixels defined by this operation consist of1274 * a square window (of full width 2Radius+1) centered on the pixel which contains1275 * the given coordinate, in the frame of the readout. The window is defined to1276 * have limits which are valid within the boundary of the readout image, thus if1277 * the radius would fall outside the image pixels, the subimage is truncated to1278 * only consist of valid pixels. If readout->mask or readout->weight are not1279 * NULL, matching subimages are defined for those images as well. This function1280 * fails if no valid pixels can be defined (x or y less than Radius, for1281 * example). This function should be used to define a region of interest around a1282 * source, including both source and sky pixels.1283 *1284 * XXX: must code this.1285 *1286 */1287 bool pmSourceDefinePixels(1288 pmSource *mySource, ///< Add comment.1289 pmReadout *readout, ///< Add comment.1290 psF32 x, ///< Add comment.1291 psF32 y, ///< Add comment.1292 psF32 Radius) ///< Add comment.1293 {1294 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);1295 psLogMsg(__func__, PS_LOG_WARN, "WARNING: pmSourceDefinePixels() has not been implemented. Returning FALSE.\n");1296 psTrace(__func__, 3, "---- %s() end ----\n", __func__);1297 return(false);1298 }1299 1300 /******************************************************************************1301 pmSourceSetPixelsCircle(source, image, radius)1302 1303 XXX: This was replaced by DefinePixels in SDRS. Remove it.1304 *****************************************************************************/1305 bool pmSourceSetPixelsCircle(pmSource *source,1306 const psImage *image,1307 psF32 radius)1308 {1309 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);1310 PS_ASSERT_IMAGE_NON_NULL(image, false);1311 PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);1312 PS_ASSERT_PTR_NON_NULL(source, false);1313 PS_ASSERT_PTR_NON_NULL(source->moments, false);1314 PS_ASSERT_PTR_NON_NULL(source->peak, false);1315 PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(radius, 0.0, false);1316 1317 //1318 // We define variables for code readability.1319 //1320 // XXX: Since the peak->xy coords are in image, not subImage coords,1321 // these variables should be renamed for clarity (imageCenterRow, etc).1322 //1323 psS32 radiusS32 = (psS32) radius;1324 psS32 SubImageCenterRow = source->peak->y;1325 psS32 SubImageCenterCol = source->peak->x;1326 // XXX EAM : for the circle to stay on the image1327 // XXX EAM : EndRow is *exclusive* of pixel region (ie, last pixel + 1)1328 psS32 SubImageStartRow = PS_MAX (0, SubImageCenterRow - radiusS32);1329 psS32 SubImageEndRow = PS_MIN (image->numRows, SubImageCenterRow + radiusS32 + 1);1330 psS32 SubImageStartCol = PS_MAX (0, SubImageCenterCol - radiusS32);1331 psS32 SubImageEndCol = PS_MIN (image->numCols, SubImageCenterCol + radiusS32 + 1);1332 1333 // XXX: Must recycle image.1334 // XXX EAM: this message reflects a programming error we know about.1335 // i am setting it to a trace message which we can take out1336 if (source->pixels != NULL) {1337 psTrace (".psModule.pmObjects.pmSourceSetPixelsCircle", 4,1338 "WARNING: pmSourceSetPixelsCircle(): image->pixels not NULL. Freeing and reallocating.\n");1339 psFree(source->pixels);1340 }1341 source->pixels = psImageSubset((psImage *) image, psRegionSet(SubImageStartCol,1342 SubImageStartRow,1343 SubImageEndCol,1344 SubImageEndRow));1345 1346 // XXX: Must recycle image.1347 if (source->mask != NULL) {1348 psFree(source->mask);1349 }1350 source->mask = psImageAlloc(source->pixels->numCols,1351 source->pixels->numRows,1352 PS_TYPE_U8); // XXX EAM : type was F321353 1354 //1355 // Loop through the subimage mask, initialize mask to 0 or 1.1356 // XXX EAM: valid pixels should have 0, not 11357 for (psS32 row = 0 ; row < source->mask->numRows; row++) {1358 for (psS32 col = 0 ; col < source->mask->numCols; col++) {1359 1360 if (checkRadius2((psF32) radiusS32,1361 (psF32) radiusS32,1362 radius,1363 (psF32) col,1364 (psF32) row)) {1365 source->mask->data.U8[row][col] = 0;1366 } else {1367 source->mask->data.U8[row][col] = 1;1368 }1369 }1370 }1371 psTrace(__func__, 3, "---- %s(true) end ----\n", __func__);1372 return(true);1373 }1374 1375 /******************************************************************************1376 pmSourceModelGuess(source, model): This function allocates a new1377 pmModel structure based on the given modelType specified in the argument list.1378 The corresponding pmModelGuess function is returned, and used to1379 supply the values of the params array in the pmModel structure.1380 1381 XXX: Many parameters are based on the src->moments structure, which is in1382 image, not subImage coords. Therefore, the calls to the model evaluation1383 functions will be in image, not subImage coords. Remember this.1384 *****************************************************************************/1385 pmModel *pmSourceModelGuess(pmSource *source,1386 pmModelType modelType)1387 {1388 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);1389 PS_ASSERT_PTR_NON_NULL(source->moments, false);1390 PS_ASSERT_PTR_NON_NULL(source->peak, false);1391 1392 pmModel *model = pmModelAlloc(modelType);1393 1394 pmModelGuessFunc modelGuessFunc = pmModelGuessFunc_GetFunction(modelType);1395 modelGuessFunc(model, source);1396 psTrace(__func__, 3, "---- %s() end ----\n", __func__);1397 return(model);1398 }1399 1400 /******************************************************************************1401 evalModel(source, level, row): a private function which evaluates the1402 source->modelPSF function at the specified coords. The coords are subImage, not1403 image coords.1404 1405 NOTE: The coords are in subImage source->pixel coords, not image coords.1406 1407 XXX: reverse order of row,col args?1408 1409 XXX: rename all coords in this file such that their name defines whether1410 the coords is in subImage or image space.1411 1412 XXX: This should probably be a public pmModules function.1413 1414 XXX: Use static vectors for x.1415 1416 XXX: Figure out if it's (row, col) or (col, row) for the model functions.1417 1418 XXX: For a while, the first psVectorAlloc() was generating a seg fault during1419 testing. Try to reproduce that and debug.1420 *****************************************************************************/1421 1422 // XXX EAM : I have made this a public function1423 // XXX EAM : this now uses a pmModel as the input1424 // XXX EAM : it was using src->type to find the model, not model->type1425 psF32 pmModelEval(pmModel *model, psImage *image, psS32 col, psS32 row)1426 {1427 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);1428 PS_ASSERT_PTR_NON_NULL(image, false);1429 PS_ASSERT_PTR_NON_NULL(model, false);1430 PS_ASSERT_PTR_NON_NULL(model->params, false);1431 1432 // Allocate the x coordinate structure and convert row/col to image space.1433 //1434 psVector *x = psVectorAlloc(2, PS_TYPE_F32);1435 x->n = x->nalloc;1436 x->data.F32[0] = (psF32) (col + image->col0);1437 x->data.F32[1] = (psF32) (row + image->row0);1438 psF32 tmpF;1439 pmModelFunc modelFunc;1440 1441 modelFunc = pmModelFunc_GetFunction (model->type);1442 tmpF = modelFunc (NULL, model->params, x);1443 psFree(x);1444 psTrace(__func__, 3, "---- %s() end ----\n", __func__);1445 return(tmpF);1446 }1447 1448 /******************************************************************************1449 pmSourceContour(src, img, level, mode): For an input subImage, and model, this1450 routine returns a psArray of coordinates that evaluate to the specified level.1451 1452 XXX: Probably should remove the "image" argument.1453 XXX: What type should the output coordinate vectors consist of? col,row?1454 XXX: Why a pmArray output?1455 XXX: doex x,y correspond with col,row or row/col?1456 XXX: What is mode?1457 XXX: The top, bottom of the contour is not correctly determined.1458 XXX EAM : this function is using the model for the contour, but it should1459 be using only the image counts1460 *****************************************************************************/1461 psArray *pmSourceContour(pmSource *source,1462 const psImage *image,1463 psF32 level,1464 pmContourType mode)1465 {1466 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);1467 PS_ASSERT_PTR_NON_NULL(source, false);1468 PS_ASSERT_PTR_NON_NULL(image, false);1469 PS_ASSERT_PTR_NON_NULL(source->moments, false);1470 PS_ASSERT_PTR_NON_NULL(source->peak, false);1471 PS_ASSERT_PTR_NON_NULL(source->pixels, false);1472 PS_ASSERT_PTR_NON_NULL(source->modelFLT, false);1473 // XXX EAM : what is the purpose of modelPSF/modelFLT?1474 1475 //1476 // Allocate data for x/y pairs.1477 //1478 psVector *xVec = psVectorAlloc(2 * source->pixels->numRows, PS_TYPE_F32);1479 psVector *yVec = psVectorAlloc(2 * source->pixels->numRows, PS_TYPE_F32);1480 xVec->n = xVec->nalloc;1481 yVec->n = yVec->nalloc;1482 //1483 // Start at the row with peak pixel, then decrement.1484 //1485 psS32 col = source->peak->x;1486 for (psS32 row = source->peak->y; row>= 0 ; row--) {1487 // XXX: yVec contain no real information. Do we really need it?1488 yVec->data.F32[row] = (psF32) (source->pixels->row0 + row);1489 yVec->data.F32[row+yVec->n] = (psF32) (source->pixels->row0 + row);1490 1491 // Starting at peak pixel, search leftwards for the column intercept.1492 psF32 leftIntercept = findValue(source, level, row, col, 0);1493 if (isnan(leftIntercept)) {1494 psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");1495 psFree(xVec);1496 psFree(yVec);1497 psTrace(__func__, 3, "---- %s(NULL) end ----\n", __func__);1498 return(NULL);1499 //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");1500 }1501 xVec->data.F32[row] = ((psF32) source->pixels->col0) + leftIntercept;1502 1503 // Starting at peak pixel, search rightwards for the column intercept.1504 1505 psF32 rightIntercept = findValue(source, level, row, col, 1);1506 if (isnan(rightIntercept)) {1507 psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");1508 psFree(xVec);1509 psFree(yVec);1510 psTrace(__func__, 3, "---- %s(NULL) end ----\n", __func__);1511 return(NULL);1512 //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");1513 }1514 psTrace(__func__, 4, "The intercepts are (%.2f, %.2f)\n", leftIntercept, rightIntercept);1515 xVec->data.F32[row+xVec->n] = ((psF32) source->pixels->col0) + rightIntercept;1516 1517 // Set starting column for next row1518 col = (psS32) ((leftIntercept + rightIntercept) / 2.0);1519 }1520 //1521 // Start at the row (+1) with peak pixel, then increment.1522 //1523 col = source->peak->x;1524 for (psS32 row = 1 + source->peak->y; row < source->pixels->numRows ; row++) {1525 // XXX: yVec contain no real information. Do we really need it?1526 yVec->data.F32[row] = (psF32) (source->pixels->row0 + row);1527 yVec->data.F32[row+yVec->n] = (psF32) (source->pixels->row0 + row);1528 1529 // Starting at peak pixel, search leftwards for the column intercept.1530 psF32 leftIntercept = findValue(source, level, row, col, 0);1531 if (isnan(leftIntercept)) {1532 psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");1533 psFree(xVec);1534 psFree(yVec);1535 psTrace(__func__, 3, "---- %s(NULL) end ----\n", __func__);1536 return(NULL);1537 //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");1538 }1539 xVec->data.F32[row] = ((psF32) source->pixels->col0) + leftIntercept;1540 1541 // Starting at peak pixel, search rightwards for the column intercept.1542 psF32 rightIntercept = findValue(source, level, row, col, 1);1543 if (isnan(rightIntercept)) {1544 psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");1545 psFree(xVec);1546 psFree(yVec);1547 psTrace(__func__, 3, "---- %s(NULL) end ----\n", __func__);1548 return(NULL);1549 //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");1550 }1551 xVec->data.F32[row+xVec->n] = ((psF32) source->pixels->col0) + rightIntercept;1552 1553 // Set starting column for next row1554 col = (psS32) ((leftIntercept + rightIntercept) / 2.0);1555 }1556 1557 //1558 // Allocate an array for result, store coord vectors there.1559 //1560 psArray *tmpArray = psArrayAlloc(2);1561 tmpArray->n = 2;1562 tmpArray->data[0] = (psPtr *) yVec;1563 tmpArray->data[1] = (psPtr *) xVec;1564 psTrace(__func__, 3, "---- %s() end ----\n", __func__);1565 return(tmpArray);1566 }1567 1568 // XXX EAM : these are better starting values, but should be available from metadata?1569 #define PM_SOURCE_FIT_MODEL_NUM_ITERATIONS 151570 #define PM_SOURCE_FIT_MODEL_TOLERANCE 0.11571 /******************************************************************************1572 pmSourceFitModel(source, model): must create the appropiate arguments to the1573 LM minimization routines for the various p_pmMinLM_XXXXXX_Vec() functions.1574 1575 XXX: should there be a mask value?1576 XXX EAM : fit the specified model (not necessarily the one in source)1577 *****************************************************************************/1578 bool pmSourceFitModel_v5(pmSource *source,1579 pmModel *model,1580 const bool PSF)1581 {1582 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);1583 PS_ASSERT_PTR_NON_NULL(source, false);1584 PS_ASSERT_PTR_NON_NULL(source->moments, false);1585 PS_ASSERT_PTR_NON_NULL(source->peak, false);1586 PS_ASSERT_PTR_NON_NULL(source->pixels, false);1587 PS_ASSERT_PTR_NON_NULL(source->mask, false);1588 PS_ASSERT_PTR_NON_NULL(source->weight, false);1589 psBool fitStatus = true;1590 psBool onPic = true;1591 psBool rc = true;1592 1593 // XXX EAM : is it necessary for the mask & weight to exist? the1594 // tests below could be conditions (!NULL)1595 1596 psVector *params = model->params;1597 psVector *dparams = model->dparams;1598 psVector *paramMask = NULL;1599 1600 pmModelFunc modelFunc = pmModelFunc_GetFunction (model->type);1601 1602 int nParams = PSF ? params->n - 4 : params->n;1603 1604 // find the number of valid pixels1605 // XXX EAM : this loop and the loop below could just be one pass1606 // using the psArrayAdd and psVectorExtend functions1607 psS32 count = 0;1608 for (psS32 i = 0; i < source->pixels->numRows; i++) {1609 for (psS32 j = 0; j < source->pixels->numCols; j++) {1610 if (source->mask->data.U8[i][j] == 0) {1611 count++;1612 }1613 }1614 }1615 if (count < nParams + 1) {1616 psTrace (".pmObjects.pmSourceFitModel", 4, "insufficient valid pixels\n");1617 psTrace(__func__, 3, "---- %s(false) end ----\n", __func__);1618 model->status = PM_MODEL_BADARGS;1619 return(false);1620 }1621 1622 // construct the coordinate and value entries1623 psArray *x = psArrayAlloc(count);1624 psVector *y = psVectorAlloc(count, PS_TYPE_F32);1625 psVector *yErr = psVectorAlloc(count, PS_TYPE_F32);1626 x->n = x->nalloc;1627 y->n = y->nalloc;1628 yErr->n = yErr->nalloc;1629 psS32 tmpCnt = 0;1630 for (psS32 i = 0; i < source->pixels->numRows; i++) {1631 for (psS32 j = 0; j < source->pixels->numCols; j++) {1632 if (source->mask->data.U8[i][j] == 0) {1633 psVector *coord = psVectorAlloc(2, PS_TYPE_F32);1634 coord->n = 2;1635 // XXX: Convert i/j to image space:1636 // XXX EAM: coord order is (x,y) == (col,row)1637 coord->data.F32[0] = (psF32) (j + source->pixels->col0);1638 coord->data.F32[1] = (psF32) (i + source->pixels->row0);1639 x->data[tmpCnt] = (psPtr *) coord;1640 y->data.F32[tmpCnt] = source->pixels->data.F32[i][j];1641 yErr->data.F32[tmpCnt] = sqrt (source->weight->data.F32[i][j]);1642 // XXX EAM : note the wasted effort: we carry dY^2, take sqrt(), then1643 // the minimization function calculates sq()1644 tmpCnt++;1645 }1646 }1647 }1648 1649 psMinimization *myMin = psMinimizationAlloc(PM_SOURCE_FIT_MODEL_NUM_ITERATIONS,1650 PM_SOURCE_FIT_MODEL_TOLERANCE);1651 1652 // PSF model only fits first 4 parameters, FLT model fits all1653 if (PSF) {1654 paramMask = psVectorAlloc (params->n, PS_TYPE_U8);1655 paramMask->n = paramMask->nalloc;1656 for (int i = 0; i < 4; i++) {1657 paramMask->data.U8[i] = 0;1658 }1659 for (int i = 4; i < paramMask->n; i++) {1660 paramMask->data.U8[i] = 1;1661 }1662 }1663 1664 // XXX EAM : covar must be F64?1665 psImage *covar = psImageAlloc (params->n, params->n, PS_TYPE_F64);1666 1667 psTrace (".pmObjects.pmSourceFitModel", 5, "fitting function\n");1668 1669 psMinConstrain *constrain = psMinConstrainAlloc();1670 constrain->paramMask = paramMask;1671 fitStatus = psMinimizeLMChi2(myMin, covar, params, constrain,1672 x, y, yErr, modelFunc);1673 psFree(constrain);1674 for (int i = 0; i < dparams->n; i++) {1675 if ((paramMask != NULL) && paramMask->data.U8[i])1676 continue;1677 dparams->data.F32[i] = sqrt(covar->data.F64[i][i]);1678 }1679 1680 // save the resulting chisq, nDOF, nIter1681 model->chisq = myMin->value;1682 model->nIter = myMin->iter;1683 model->nDOF = y->n - nParams;1684 1685 // get the Gauss-Newton distance for fixed model parameters1686 if (paramMask != NULL) {1687 psVector *delta = psVectorAlloc (params->n, PS_TYPE_F64);1688 delta->n = delta->nalloc;1689 psMinimizeGaussNewtonDelta (delta, params, NULL, x, y, yErr, modelFunc);1690 for (int i = 0; i < dparams->n; i++) {1691 if (!paramMask->data.U8[i])1692 continue;1693 dparams->data.F32[i] = delta->data.F64[i];1694 }1695 psFree (delta);1696 }1697 1698 // set the model success or failure status1699 if (!fitStatus) {1700 model->status = PM_MODEL_NONCONVERGE;1701 } else {1702 model->status = PM_MODEL_SUCCESS;1703 }1704 1705 // models can go insane: reject these1706 onPic &= (params->data.F32[2] >= source->pixels->col0);1707 onPic &= (params->data.F32[2] < source->pixels->col0 + source->pixels->numCols);1708 onPic &= (params->data.F32[3] >= source->pixels->row0);1709 onPic &= (params->data.F32[3] < source->pixels->row0 + source->pixels->numRows);1710 if (!onPic) {1711 model->status = PM_MODEL_OFFIMAGE;1712 }1713 1714 psFree(x);1715 psFree(y);1716 psFree(yErr);1717 psFree(myMin);1718 psFree(covar);1719 psFree(paramMask);1720 1721 rc = (onPic && fitStatus);1722 psTrace(__func__, 3, "---- %s(%d) end ----\n", __func__, rc);1723 return(rc);1724 }1725 1726 // XXX EAM : new version with parameter range limits and weight enhancement1727 bool pmSourceFitModel (pmSource *source,1728 pmModel *model,1729 const bool PSF)1730 {1731 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);1732 PS_ASSERT_PTR_NON_NULL(source, false);1733 PS_ASSERT_PTR_NON_NULL(source->moments, false);1734 PS_ASSERT_PTR_NON_NULL(source->peak, false);1735 PS_ASSERT_PTR_NON_NULL(source->pixels, false);1736 PS_ASSERT_PTR_NON_NULL(source->mask, false);1737 PS_ASSERT_PTR_NON_NULL(source->weight, false);1738 1739 // XXX EAM : is it necessary for the mask & weight to exist? the1740 // tests below could be conditions (!NULL)1741 1742 psBool fitStatus = true;1743 psBool onPic = true;1744 psBool rc = true;1745 psF32 Ro, ymodel;1746 1747 psVector *params = model->params;1748 psVector *dparams = model->dparams;1749 psVector *paramMask = NULL;1750 1751 pmModelFunc modelFunc = pmModelFunc_GetFunction (model->type);1752 1753 // XXX EAM : I need to use the sky value to constrain the weight model1754 int nParams = PSF ? params->n - 4 : params->n;1755 psF32 So = params->data.F32[0];1756 1757 // find the number of valid pixels1758 // XXX EAM : this loop and the loop below could just be one pass1759 // using the psArrayAdd and psVectorExtend functions1760 psS32 count = 0;1761 for (psS32 i = 0; i < source->pixels->numRows; i++) {1762 for (psS32 j = 0; j < source->pixels->numCols; j++) {1763 if (source->mask->data.U8[i][j] == 0) {1764 count++;1765 }1766 }1767 }1768 if (count < nParams + 1) {1769 psTrace (".pmObjects.pmSourceFitModel", 4, "insufficient valid pixels\n");1770 psTrace(__func__, 3, "---- %s(false) end ----\n", __func__);1771 model->status = PM_MODEL_BADARGS;1772 return(false);1773 }1774 1775 // construct the coordinate and value entries1776 psArray *x = psArrayAlloc(count);1777 psVector *y = psVectorAlloc(count, PS_TYPE_F32);1778 psVector *yErr = psVectorAlloc(count, PS_TYPE_F32);1779 x->n = x->nalloc;1780 y->n = y->nalloc;1781 yErr->n = yErr->nalloc;1782 psS32 tmpCnt = 0;1783 for (psS32 i = 0; i < source->pixels->numRows; i++) {1784 for (psS32 j = 0; j < source->pixels->numCols; j++) {1785 if (source->mask->data.U8[i][j] == 0) {1786 psVector *coord = psVectorAlloc(2, PS_TYPE_F32);1787 coord->n = 2;1788 // XXX: Convert i/j to image space:1789 // XXX EAM: coord order is (x,y) == (col,row)1790 coord->data.F32[0] = (psF32) (j + source->pixels->col0);1791 coord->data.F32[1] = (psF32) (i + source->pixels->row0);1792 x->data[tmpCnt] = (psPtr *) coord;1793 y->data.F32[tmpCnt] = source->pixels->data.F32[i][j];1794 1795 // compare observed flux to model flux to adjust weight1796 ymodel = modelFunc (NULL, model->params, coord);1797 1798 // this test enhances the weight based on deviation from the model flux1799 Ro = 1.0 + fabs (y->data.F32[tmpCnt] - ymodel) / sqrt(PS_SQR(ymodel - So)1800 + PS_SQR(So));1801 1802 // psMinimizeLMChi2_EAM takes wt = 1/dY^21803 if (source->weight->data.F32[i][j] == 0) {1804 yErr->data.F32[tmpCnt] = 0.0;1805 } else {1806 yErr->data.F32[tmpCnt] = 1.0 / (source->weight->data.F32[i][j] * Ro);1807 }1808 tmpCnt++;1809 }1810 }1811 }1812 1813 psMinimization *myMin = psMinimizationAlloc(PM_SOURCE_FIT_MODEL_NUM_ITERATIONS,1814 PM_SOURCE_FIT_MODEL_TOLERANCE);1815 1816 // PSF model only fits first 4 parameters, FLT model fits all1817 if (PSF) {1818 paramMask = psVectorAlloc (params->n, PS_TYPE_U8);1819 paramMask->n = paramMask->nalloc;1820 for (int i = 0; i < 4; i++) {1821 paramMask->data.U8[i] = 0;1822 }1823 for (int i = 4; i < paramMask->n; i++) {1824 paramMask->data.U8[i] = 1;1825 }1826 }1827 1828 // XXX EAM : I've added three types of parameter range checks1829 // XXX EAM : this requires my new psMinimization functions1830 pmModelLimits modelLimits = pmModelLimits_GetFunction (model->type);1831 psVector *beta_lim = NULL;1832 psVector *params_min = NULL;1833 psVector *params_max = NULL;1834 1835 // XXX EAM : in this implementation, I pass in the limits with the covar matrix.1836 // in the SDRS, I define a new psMinimization which will take these in1837 psImage *covar = psImageAlloc (params->n, 3, PS_TYPE_F64);1838 modelLimits (&beta_lim, ¶ms_min, ¶ms_max);1839 for (int i = 0; i < params->n; i++) {1840 covar->data.F64[0][i] = beta_lim->data.F32[i];1841 covar->data.F64[1][i] = params_min->data.F32[i];1842 covar->data.F64[2][i] = params_max->data.F32[i];1843 }1844 1845 psTrace (".pmObjects.pmSourceFitModel", 5, "fitting function\n");1846 psMinConstrain *constrain = psMinConstrainAlloc();1847 constrain->paramMask = paramMask;1848 fitStatus = psMinimizeLMChi2(myMin, covar, params, constrain,1849 x, y, yErr, modelFunc);1850 psFree(constrain);1851 for (int i = 0; i < dparams->n; i++) {1852 if ((paramMask != NULL) && paramMask->data.U8[i])1853 continue;1854 dparams->data.F32[i] = sqrt(covar->data.F64[i][i]);1855 }1856 1857 // XXX EAM: we need to do something (give an error?) if rc is false1858 // XXX EAM: psMinimizeLMChi2 does not check convergence1859 1860 // XXX EAM: save the resulting chisq, nDOF, nIter1861 model->chisq = myMin->value;1862 model->nIter = myMin->iter;1863 model->nDOF = y->n - nParams;1864 1865 // XXX EAM get the Gauss-Newton distance for fixed model parameters1866 if (paramMask != NULL) {1867 psVector *delta = psVectorAlloc (params->n, PS_TYPE_F64);1868 delta->n = delta->nalloc;1869 psMinimizeGaussNewtonDelta(delta, params, NULL, x, y, yErr, modelFunc);1870 for (int i = 0; i < dparams->n; i++) {1871 if (!paramMask->data.U8[i])1872 continue;1873 dparams->data.F32[i] = delta->data.F64[i];1874 }1875 }1876 1877 // set the model success or failure status1878 if (!fitStatus) {1879 model->status = PM_MODEL_NONCONVERGE;1880 } else {1881 model->status = PM_MODEL_SUCCESS;1882 }1883 1884 // XXX models can go insane: reject these1885 onPic &= (params->data.F32[2] >= source->pixels->col0);1886 onPic &= (params->data.F32[2] < source->pixels->col0 + source->pixels->numCols);1887 onPic &= (params->data.F32[3] >= source->pixels->row0);1888 onPic &= (params->data.F32[3] < source->pixels->row0 + source->pixels->numRows);1889 if (!onPic) {1890 model->status = PM_MODEL_OFFIMAGE;1891 }1892 1893 psFree(x);1894 psFree(y);1895 psFree(yErr);1896 psFree(myMin);1897 psFree(covar);1898 psFree(paramMask);1899 1900 rc = (onPic && fitStatus);1901 psTrace(__func__, 3, "---- %s(%d) end ----\n", __func__, rc);1902 return(rc);1903 }1904 1905 bool p_pmSourceAddOrSubModel(psImage *image,1906 psImage *mask,1907 pmModel *model,1908 bool center,1909 bool sky,1910 bool add1911 )1912 {1913 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);1914 1915 PS_ASSERT_PTR_NON_NULL(model, false);1916 PS_ASSERT_IMAGE_NON_NULL(image, false);1917 PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);1918 1919 psVector *x = psVectorAlloc(2, PS_TYPE_F32);1920 x->n = 2;1921 psVector *params = model->params;1922 pmModelFunc modelFunc = pmModelFunc_GetFunction (model->type);1923 psS32 imageCol;1924 psS32 imageRow;1925 psF32 skyValue = params->data.F32[0];1926 psF32 pixelValue;1927 1928 for (psS32 i = 0; i < image->numRows; i++) {1929 for (psS32 j = 0; j < image->numCols; j++) {1930 if ((mask != NULL) && mask->data.U8[i][j])1931 continue;1932 1933 // XXX: Should you be adding the pixels for the entire subImage,1934 // or a radius of pixels around it?1935 1936 // Convert i/j to imace coord space:1937 // XXX: Make sure you have col/row order correct.1938 // XXX EAM : 'center' option changes this1939 // XXX EAM : i == numCols/2 -> x = model->params->data.F32[2]1940 if (center) {1941 imageCol = j - 0.5*image->numCols + model->params->data.F32[2];1942 imageRow = i - 0.5*image->numRows + model->params->data.F32[3];1943 } else {1944 imageCol = j + image->col0;1945 imageRow = i + image->row0;1946 }1947 1948 x->data.F32[0] = (float) imageCol;1949 x->data.F32[1] = (float) imageRow;1950 1951 // set the appropriate pixel value for this coordinate1952 if (sky) {1953 pixelValue = modelFunc (NULL, params, x);1954 } else {1955 pixelValue = modelFunc (NULL, params, x) - skyValue;1956 }1957 1958 1959 // add or subtract the value1960 if (add1961 ) {1962 image->data.F32[i][j] += pixelValue;1963 }1964 else {1965 image->data.F32[i][j] -= pixelValue;1966 }1967 }1968 }1969 psFree(x);1970 psTrace(__func__, 3, "---- %s(true) end ----\n", __func__);1971 return(true);1972 }1973 1974 1975 1976 /******************************************************************************1977 *****************************************************************************/1978 bool pmSourceAddModel(psImage *image,1979 psImage *mask,1980 pmModel *model,1981 bool center,1982 bool sky)1983 {1984 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);1985 psBool rc = p_pmSourceAddOrSubModel(image, mask, model, center, sky, true);1986 psTrace(__func__, 3, "---- %s(%d) end ----\n", __func__, rc);1987 return(rc);1988 }1989 1990 /******************************************************************************1991 *****************************************************************************/1992 bool pmSourceSubModel(psImage *image,1993 psImage *mask,1994 pmModel *model,1995 bool center,1996 bool sky)1997 {1998 psTrace(__func__, 3, "---- %s() begin ----\n", __func__);1999 psBool rc = p_pmSourceAddOrSubModel(image, mask, model, center, sky, false);2000 psTrace(__func__, 3, "---- %s(%d) end ----\n", __func__, rc);2001 return(rc);2002 }2003 2004 bool pmSourcePhotometry (float *fitMag, float *obsMag, pmModel *model, psImage *image, psImage *mask)2005 {2006 2007 float obsSum = 0;2008 float fitSum = 0;2009 float sky = model->params->data.F32[0];2010 2011 pmModelFlux modelFluxFunc = pmModelFlux_GetFunction (model->type);2012 fitSum = modelFluxFunc (model->params);2013 2014 for (int ix = 0; ix < image->numCols; ix++) {2015 for (int iy = 0; iy < image->numRows; iy++) {2016 if (mask->data.U8[iy][ix])2017 continue;2018 obsSum += image->data.F32[iy][ix] - sky;2019 }2020 }2021 if (obsSum <= 0)2022 return false;2023 if (fitSum <= 0)2024 return false;2025 2026 *fitMag = -2.5*log10(fitSum);2027 *obsMag = -2.5*log10(obsSum);2028 return (true);2029 }2030 -
trunk/psModules/src/objects/pmPSF.c
r6511 r6873 6 6 * @author EAM, IfA 7 7 * 8 * @version $Revision: 1. 5$ $Name: not supported by cvs2svn $9 * @date $Date: 2006-0 3-04 01:01:33$8 * @version $Revision: 1.6 $ $Name: not supported by cvs2svn $ 9 * @date $Date: 2006-04-17 18:10:08 $ 10 10 * 11 11 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii … … 18 18 19 19 #include <pslib.h> 20 #include "pmObjects.h" 20 #include "pmHDU.h" 21 #include "pmFPA.h" 22 #include "pmPeaks.h" 23 #include "pmMoments.h" 24 #include "pmModel.h" 25 #include "pmSource.h" 26 #include "pmGrowthCurve.h" 21 27 #include "pmPSF.h" 22 28 #include "pmModelGroup.h" … … 57 63 return; 58 64 65 psFree (psf->ApTrend); 66 psFree (psf->growth); 59 67 psFree (psf->params); 60 68 return; … … 69 77 X-center 70 78 Y-center 71 ???: Sky background value?72 ???: Zo?79 Sky background value 80 Object Normalization 73 81 *****************************************************************************/ 74 pmPSF *pmPSFAlloc (pmModelType type )82 pmPSF *pmPSFAlloc (pmModelType type, bool poissonErrors) 75 83 { 76 84 int Nparams; … … 83 91 psf->dApResid = 0.0; 84 92 psf->skyBias = 0.0; 93 psf->skySat = 0.0; 94 psf->poissonErrors = poissonErrors; 95 96 // the ApTrend components are (x, y, r2rflux, flux) 97 psf->ApTrend = psPolynomial4DAlloc (PS_POLYNOMIAL_ORD, 2, 2, 1, 1); 98 pmPSF_MaskApTrend (psf, PM_PSF_SKYBIAS); 99 100 if (psf->poissonErrors) { 101 psf->ChiTrend = psPolynomial1DAlloc (PS_POLYNOMIAL_ORD, 1); 102 } else { 103 psf->ChiTrend = psPolynomial1DAlloc (PS_POLYNOMIAL_ORD, 2); 104 } 105 106 // don't define a growth curve : user needs to choose radius bins 107 psf->growth = NULL; 85 108 86 109 Nparams = pmModelParameterCount (type); … … 94 117 for (int i = 0; i < psf->params->n; i++) { 95 118 // XXX EAM : make this a user-defined value? 96 psf->params->data[i] = psPolynomial2DAlloc( 1, 1, PS_POLYNOMIAL_ORD);119 psf->params->data[i] = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 1, 1); 97 120 } 98 121 … … 174 197 175 198 /***************************************************************************** 176 pmModelFromPSF (*model FLT, *psf): use the model position parameters to199 pmModelFromPSF (*modelEXT, *psf): use the model position parameters to 177 200 construct a realization of the PSF model at the object coordinates 178 201 *****************************************************************************/ 179 pmModel *pmModelFromPSF (pmModel *model FLT, pmPSF *psf)180 { 181 182 // need to define the relationship between the model FLT and modelPSF ?202 pmModel *pmModelFromPSF (pmModel *modelEXT, pmPSF *psf) 203 { 204 205 // need to define the relationship between the modelEXT and modelPSF ? 183 206 184 207 // find function used to set the model parameters … … 189 212 190 213 // set model parameters for this source based on PSF information 191 modelFromPSFFunc (modelPSF, model FLT, psf);214 modelFromPSFFunc (modelPSF, modelEXT, psf); 192 215 193 216 return (modelPSF); 194 217 } 218 219 // zero and mask out all terms: 220 static bool maskAllTerms (psPolynomial4D *trend) 221 { 222 223 for (int i = 0; i < trend->nX + 1; i++) { 224 for (int j = 0; j < trend->nY + 1; j++) { 225 for (int k = 0; k < trend->nZ + 1; k++) { 226 for (int m = 0; m < trend->nT + 1; m++) { 227 trend->mask[i][j][k][m] = 1; // mask coeff 228 trend->coeff[i][j][k][m] = 0; // zero coeff 229 } 230 } 231 } 232 } 233 return true; 234 } 235 236 /*********************************************** 237 * this function masks the psf.ApTrend polynomial 238 * to enable the specific subset of the coefficients 239 **********************************************/ 240 bool pmPSF_MaskApTrend (pmPSF *psf, pmPSF_ApTrendOptions option) 241 { 242 243 switch (option) { 244 case PM_PSF_NONE: 245 maskAllTerms (psf->ApTrend); 246 return true; 247 248 case PM_PSF_CONSTANT: 249 maskAllTerms (psf->ApTrend); 250 psf->ApTrend->mask[0][0][0][0] = 0; // unmask constant 251 return true; 252 253 case PM_PSF_SKYBIAS: 254 maskAllTerms (psf->ApTrend); 255 psf->ApTrend->mask[0][0][0][0] = 0; // unmask constant 256 psf->ApTrend->mask[0][0][1][0] = 0; // unmask skybias 257 return true; 258 259 case PM_PSF_SKYSAT: 260 maskAllTerms (psf->ApTrend); 261 psf->ApTrend->mask[0][0][0][0] = 0; // unmask constant 262 psf->ApTrend->mask[0][0][1][0] = 0; // unmask skybias 263 psf->ApTrend->mask[0][0][0][1] = 0; // unmask skybias 264 return true; 265 266 case PM_PSF_XY_LIN: 267 maskAllTerms (psf->ApTrend); 268 psf->ApTrend->mask[0][0][0][0] = 0; // unmask constant 269 psf->ApTrend->mask[1][0][0][0] = 0; // unmask x 270 psf->ApTrend->mask[0][1][0][0] = 0; // unmask y 271 return true; 272 273 case PM_PSF_XY_QUAD: 274 maskAllTerms (psf->ApTrend); 275 psf->ApTrend->mask[0][0][0][0] = 0; // unmask constant 276 psf->ApTrend->mask[1][0][0][0] = 0; // unmask x 277 psf->ApTrend->mask[2][0][0][0] = 0; // unmask x^2 278 psf->ApTrend->mask[1][1][0][0] = 0; // unmask x y 279 psf->ApTrend->mask[0][1][0][0] = 0; // unmask y 280 psf->ApTrend->mask[0][2][0][0] = 0; // unmask y^2 281 return true; 282 283 case PM_PSF_SKY_XY_LIN: 284 maskAllTerms (psf->ApTrend); 285 psf->ApTrend->mask[0][0][0][0] = 0; // unmask constant 286 psf->ApTrend->mask[1][0][0][0] = 0; // unmask x 287 psf->ApTrend->mask[0][1][0][0] = 0; // unmask y 288 psf->ApTrend->mask[0][0][1][0] = 0; // unmask skybias 289 return true; 290 291 case PM_PSF_SKYSAT_XY_LIN: 292 maskAllTerms (psf->ApTrend); 293 psf->ApTrend->mask[0][0][0][0] = 0; // unmask constant 294 psf->ApTrend->mask[1][0][0][0] = 0; // unmask x 295 psf->ApTrend->mask[0][1][0][0] = 0; // unmask y 296 psf->ApTrend->mask[0][0][1][0] = 0; // unmask skybias 297 psf->ApTrend->mask[0][0][0][1] = 0; // unmask skysat 298 return true; 299 300 case PM_PSF_ALL: 301 default: 302 maskAllTerms (psf->ApTrend); 303 psf->ApTrend->mask[0][0][0][0] = 0; // unmask constant 304 psf->ApTrend->mask[0][0][1][0] = 0; // unmask skybias 305 psf->ApTrend->mask[0][0][0][1] = 0; // unmask skysat 306 307 psf->ApTrend->mask[1][0][0][0] = 0; // unmask x 308 psf->ApTrend->mask[2][0][0][0] = 0; // unmask x^2 309 psf->ApTrend->mask[1][1][0][0] = 0; // unmask x y 310 psf->ApTrend->mask[0][1][0][0] = 0; // unmask y 311 psf->ApTrend->mask[0][2][0][0] = 0; // unmask y^2 312 return true; 313 } 314 return false; 315 } -
trunk/psModules/src/objects/pmPSF.h
r6872 r6873 6 6 * @author EAM, IfA 7 7 * 8 * @version $Revision: 1.2 $ $Name: not supported by cvs2svn $9 * @date $Date: 2006-04-17 18:01:05 $10 8 * 11 9 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii -
trunk/psModules/src/objects/pmPSFtry.c
r6511 r6873 5 5 * @author EAM, IfA 6 6 * 7 * @version $Revision: 1. 5$ $Name: not supported by cvs2svn $8 * @date $Date: 2006-0 3-04 01:01:33$7 * @version $Revision: 1.6 $ $Name: not supported by cvs2svn $ 8 * @date $Date: 2006-04-17 18:10:08 $ 9 9 * 10 10 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii … … 13 13 14 14 # include <pslib.h> 15 # include "pmObjects.h" 16 # include "pmPSF.h" 17 # include "pmPSFtry.h" 18 # include "pmModelGroup.h" 15 #include "pmHDU.h" 16 #include "pmFPA.h" 17 #include "pmPeaks.h" 18 #include "pmMoments.h" 19 #include "pmModel.h" 20 #include "pmSource.h" 21 #include "pmGrowthCurve.h" 22 #include "pmPSF.h" 23 #include "pmPSFtry.h" 24 #include "pmModelGroup.h" 25 #include "pmSourceFitModel.h" 26 #include "pmSourcePhotometry.h" 19 27 20 28 // ******** pmPSFtry functions ************************************************** … … 33 41 psFree (test->psf); 34 42 psFree (test->sources); 35 psFree (test->model FLT);43 psFree (test->modelEXT); 36 44 psFree (test->modelPSF); 37 45 psFree (test->metric); … … 42 50 43 51 // allocate a pmPSFtry based on the desired sources and the model (identified by name) 44 pmPSFtry *pmPSFtryAlloc (psArray *sources, char *modelName )52 pmPSFtry *pmPSFtryAlloc (psArray *sources, char *modelName, bool poissonErrors) 45 53 { 46 54 … … 51 59 // XXX probably need to increment ref counter 52 60 type = pmModelSetType (modelName); 53 test->psf = pmPSFAlloc (type );61 test->psf = pmPSFAlloc (type, poissonErrors); 54 62 test->sources = psMemIncrRefCounter(sources); 55 test->model FLT = psArrayAlloc (sources->n);63 test->modelEXT = psArrayAlloc (sources->n); 56 64 test->modelPSF = psArrayAlloc (sources->n); 57 65 test->metric = psVectorAlloc (sources->n, PS_TYPE_F64); … … 59 67 test->mask = psVectorAlloc (sources->n, PS_TYPE_U8); 60 68 61 test->modelFLT->n = test->modelFLT->nalloc; 62 test->modelPSF->n = test->modelPSF->nalloc; 63 test->metric->n = test->metric->nalloc; 64 test->fitMag->n = test->fitMag->nalloc; 65 test->mask->n = test->mask->nalloc; 66 67 for (int i = 0; i < test->modelFLT->n; i++) { 69 for (int i = 0; i < test->modelEXT->n; i++) { 68 70 test->mask->data.U8[i] = 0; 69 test->model FLT->data[i] = NULL;71 test->modelEXT->data[i] = NULL; 70 72 test->modelPSF->data[i] = NULL; 71 73 test->metric->data.F64[i] = 0; … … 86 88 // mask values indicate the reason the source was rejected: 87 89 88 pmPSFtry *pmPSFtryModel (psArray *sources, char *modelName, float RADIUS )90 pmPSFtry *pmPSFtryModel (psArray *sources, char *modelName, float RADIUS, bool poissonErrors) 89 91 { 90 92 bool status; … … 93 95 float x; 94 96 float y; 95 int N flt = 0;97 int Next = 0; 96 98 int Npsf = 0; 97 99 98 pmPSFtry *psfTry = pmPSFtryAlloc (sources, modelName );100 pmPSFtry *psfTry = pmPSFtryAlloc (sources, modelName, poissonErrors); 99 101 100 102 // stage 1: fit an independent model (freeModel) to all sources … … 108 110 109 111 // set temporary object mask and fit object 110 // fit model as FLT, not PSF 111 psImageKeepCircle (source->mask, x, y, RADIUS, "OR", PSPHOT_MASK_MARKED); 112 status = pmSourceFitModel (source, model, false); 113 psImageKeepCircle (source->mask, x, y, RADIUS, "AND", ~PSPHOT_MASK_MARKED); 112 // fit model as EXT, not PSF 113 114 psImageKeepCircle (source->mask, x, y, RADIUS, "OR", PM_SOURCE_MASK_MARKED); 115 status = pmSourceFitModel (source, model, PM_SOURCE_FIT_EXT); 116 psImageKeepCircle (source->mask, x, y, RADIUS, "AND", ~PM_SOURCE_MASK_MARKED); 114 117 115 118 // exclude the poor fits 116 119 if (!status) { 117 psfTry->mask->data.U8[i] = PSFTRY_MASK_ FLT_FAIL;120 psfTry->mask->data.U8[i] = PSFTRY_MASK_EXT_FAIL; 118 121 psFree (model); 119 122 continue; 120 123 } 121 psfTry->modelFLT->data[i] = model; 122 Nflt ++; 123 } 124 psLogMsg ("psphot.psftry", 4, "fit flt: %f sec for %d sources\n", psTimerMark ("fit"), sources->n); 125 psTrace ("psphot.psftry", 3, "keeping %d of %d PSF candidates (FLT)\n", Nflt, sources->n); 126 127 // make this optional? 128 // DumpModelFits (psfTry->modelFLT, "modelsFLT.dat"); 124 psfTry->modelEXT->data[i] = model; 125 Next ++; 126 } 127 psLogMsg ("psphot.psftry", 4, "fit ext: %f sec for %d sources\n", psTimerMark ("fit"), sources->n); 128 psTrace ("psphot.psftry", 3, "keeping %d of %d PSF candidates (EXT)\n", Next, sources->n); 129 129 130 130 // stage 2: construct a psf (pmPSF) from this collection of model fits 131 pmPSFFromModels (psfTry->psf, psfTry->model FLT, psfTry->mask);131 pmPSFFromModels (psfTry->psf, psfTry->modelEXT, psfTry->mask); 132 132 133 133 // stage 3: refit with fixed shape parameters … … 139 139 140 140 pmSource *source = psfTry->sources->data[i]; 141 pmModel *model FLT = psfTry->modelFLT->data[i];141 pmModel *modelEXT = psfTry->modelEXT->data[i]; 142 142 143 143 // set shape for this model based on PSF 144 pmModel *modelPSF = pmModelFromPSF (model FLT, psfTry->psf);144 pmModel *modelPSF = pmModelFromPSF (modelEXT, psfTry->psf); 145 145 x = source->peak->x; 146 146 y = source->peak->y; 147 147 148 psImageKeepCircle (source->mask, x, y, RADIUS, "OR", P SPHOT_MASK_MARKED);149 status = pmSourceFitModel (source, modelPSF, true);148 psImageKeepCircle (source->mask, x, y, RADIUS, "OR", PM_SOURCE_MASK_MARKED); 149 status = pmSourceFitModel (source, modelPSF, PM_SOURCE_FIT_PSF); 150 150 151 151 // skip poor fits … … 162 162 // XXX : use a different estimator for the local sky? 163 163 // XXX : pass 'source' as input? 164 if (!pmSourcePhotometry (&fitMag, &obsMag, modelPSF, source->pixels, source->mask)) { 164 if (!pmSourcePhotometryModel (&fitMag, modelPSF)) { 165 psfTry->mask->data.U8[i] = PSFTRY_MASK_BAD_PHOT; 166 goto next_source; 167 } 168 if (!pmSourcePhotometryAper (&obsMag, modelPSF, source->pixels, source->mask)) { 165 169 psfTry->mask->data.U8[i] = PSFTRY_MASK_BAD_PHOT; 166 170 goto next_source; … … 172 176 173 177 next_source: 174 psImageKeepCircle (source->mask, x, y, RADIUS, "AND", ~PSPHOT_MASK_MARKED); 175 176 } 178 psImageKeepCircle (source->mask, x, y, RADIUS, "AND", ~PM_SOURCE_MASK_MARKED); 179 180 } 181 psfTry->psf->nPSFstars = Npsf; 182 177 183 psLogMsg ("psphot.psftry", 4, "fit psf: %f sec for %d sources\n", psTimerMark ("fit"), sources->n); 178 184 psTrace ("psphot.psftry", 3, "keeping %d of %d PSF candidates (PSF)\n", Npsf, sources->n); 179 185 180 // make this optional 181 // DumpModelFits (psfTry->modelPSF, "modelsPSF.dat"); 186 // measure the chi-square trend as a function of flux (PAR[1]) 187 // this should be linear for Poisson errors and quadratic for constant sky errors 188 psVector *flux = psVectorAlloc (psfTry->sources->n, PS_TYPE_F64); 189 psVector *chisq = psVectorAlloc (psfTry->sources->n, PS_TYPE_F64); 190 psVector *mask = psVectorAlloc (psfTry->sources->n, PS_TYPE_MASK); 191 192 // write sources with models first 193 for (int i = 0; i < psfTry->sources->n; i++) { 194 pmModel *model = psfTry->modelPSF->data[i]; 195 if (model == NULL) { 196 flux->data.F64[i] = 0.0; 197 chisq->data.F64[i] = 0.0; 198 mask->data.U8[i] = 1; 199 } else { 200 flux->data.F64[i] = model->params->data.F32[1]; 201 chisq->data.F64[i] = model[0].chisq/model[0].nDOF; 202 mask->data.U8[i] = 0; 203 } 204 } 205 // use 3hi/3lo sigma clipping on the r2rflux vs metric fit 206 psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV); 207 stats->clipSigma = 3.0; 208 stats->clipIter = 3; 209 // linear clipped fit of ApResid to r2rflux 210 psVectorClipFitPolynomial1D (psfTry->psf->ChiTrend, stats, mask, 1, chisq, NULL, flux); 211 for (int i = 0; i < psfTry->psf->ChiTrend->nX + 1; i++) { 212 psLogMsg ("pmPSFtry", 4, "chisq vs flux fit term %d: %f +/- %f\n", i, psfTry->psf->ChiTrend->coeff[i]*pow(10000, i), psfTry->psf->ChiTrend->coeffErr[i]); 213 } 214 psLogMsg ("pmPSFtry", 4, "chisq vs flux fit: %f +/- %f\n", stats->sampleMedian, stats->sampleStdev); 215 psFree (stats); 216 psFree (flux); 217 psFree (chisq); 182 218 183 219 // XXX this function wants aperture radius for pmSourcePhotometry 184 pmPSFtryMetric_Alt (psfTry, RADIUS); 185 psLogMsg ("psphot.pspsf", 3, "try model %s, ap-fit: %f +/- %f, sky bias: %f\n", 186 modelName, 187 psfTry->psf->ApResid, 188 psfTry->psf->dApResid, 189 psfTry->psf->skyBias); 220 pmPSFtryMetric (psfTry, RADIUS); 221 psLogMsg ("psphot.pspsf", 3, "try model %s, ap-fit: %f +/- %f : sky bias: %f\n", 222 modelName, psfTry->psf->ApResid, psfTry->psf->dApResid, psfTry->psf->skyBias); 190 223 191 224 return (psfTry); 192 225 } 193 226 194 195 227 bool pmPSFtryMetric (pmPSFtry *psfTry, float RADIUS) 196 228 { 197 198 float dBin;199 int nKeep, nSkip;200 201 229 // the measured (aperture - fit) magnitudes (dA == psfTry->metric) 202 230 // depend on both the true ap-fit (dAo) and the bias in the sky measurement: … … 208 236 // we use an outlier rejection to avoid this bias 209 237 210 // rflux = ten(0.4*fitMag); 211 psVector *rflux = psVectorAlloc (psfTry->sources->n, PS_TYPE_F64); 212 rflux->n = rflux->nalloc; 238 // r2rflux = radius^2 * ten(0.4*fitMag); 239 psVector *r2rflux = psVectorAlloc (psfTry->sources->n, PS_TYPE_F64); 213 240 for (int i = 0; i < psfTry->sources->n; i++) { 214 241 if (psfTry->mask->data.U8[i] & PSFTRY_MASK_ALL) 215 242 continue; 216 rflux->data.F64[i] = pow(10.0, 0.4*psfTry->fitMag->data.F64[i]); 217 } 218 219 // find min and max of (1/flux): 220 psStats *stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX); 221 psVectorStats (stats, rflux, NULL, psfTry->mask, PSFTRY_MASK_ALL); 222 223 // build binned versions of rflux, metric 224 dBin = (stats->max - stats->min) / 10.0; 225 psVector *rfBin = psVectorCreate(NULL, stats->min, stats->max, dBin, PS_TYPE_F64); 226 psVector *daBin = psVectorAlloc (rfBin->n, PS_TYPE_F64); 227 psVector *maskB = psVectorAlloc (rfBin->n, PS_TYPE_U8); 228 daBin->n = daBin->nalloc; 229 maskB->n = maskB->nalloc; 230 psFree (stats); 231 232 psTrace ("psphot.metricmodel", 3, "rflux max: %g, min: %g, delta: %g\n", stats->max, stats->min, dBin); 233 234 // group data in daBin bins, measure lower 50% mean 235 for (int i = 0; i < daBin->n; i++) { 236 237 psVector *tmp = psVectorAlloc (psfTry->sources->n, PS_TYPE_F64); 238 tmp->n = 0; 239 240 // accumulate data within bin range 241 for (int j = 0; j < psfTry->sources->n; j++) { 242 // masked for: bad model fit, outlier in parameters 243 if (psfTry->mask->data.U8[j] & PSFTRY_MASK_ALL) 244 continue; 245 246 // skip points with extreme dA values 247 if (fabs(psfTry->metric->data.F64[j]) > 0.5) 248 continue; 249 250 // skip points outside of this bin 251 if (rflux->data.F64[j] < rfBin->data.F64[i] - 0.5*dBin) 252 continue; 253 if (rflux->data.F64[j] > rfBin->data.F64[i] + 0.5*dBin) 254 continue; 255 256 tmp->data.F64[tmp->n] = psfTry->metric->data.F64[j]; 257 tmp->n ++; 258 } 259 260 // is this a valid point? 261 maskB->data.U8[i] = 0; 262 if (tmp->n < 2) { 263 maskB->data.U8[i] = 1; 264 psFree (tmp); 265 continue; 266 } 267 268 // dA values are contaminated with low outliers 269 // measure statistics only on upper 50% of points 270 // this would be easier if we could sort in reverse: 271 // 272 // psVectorSort (tmp, tmp); 273 // tmp->n = 0.5*tmp->n; 274 // stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN); 275 // psVectorStats (stats, tmp, NULL, NULL, 0); 276 // psTrace ("psphot.metricmodel", 4, "rfBin %d (%g): %d pts, %g\n", i, rfBin->data.F64[i], tmp->n, stats->sampleMedian); 277 278 psVectorSort (tmp, tmp); 279 nKeep = 0.5*tmp->n; 280 nSkip = tmp->n - nKeep; 281 282 psVector *tmp2 = psVectorAlloc (nKeep, PS_TYPE_F64); 283 tmp2->n = tmp2->nalloc; 284 for (int j = 0; j < tmp2->n; j++) { 285 tmp2->data.F64[j] = tmp->data.F64[j + nSkip]; 286 } 287 288 stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN); 289 psVectorStats (stats, tmp2, NULL, NULL, 0); 290 psTrace ("psphot.metricmodel", 4, "rfBin %d (%g): %d pts, %g\n", i, rfBin->data.F64[i], tmp->n, stats->sampleMedian); 291 292 daBin->data.F64[i] = stats->sampleMedian; 293 294 psFree (stats); 295 psFree (tmp); 296 psFree (tmp2); 297 } 298 299 // linear fit to rfBin, daBin 300 psPolynomial1D *poly = psPolynomial1DAlloc(1, PS_POLYNOMIAL_ORD); 301 psStats *fitstat = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV); 302 poly = psVectorClipFitPolynomial1D (poly, fitstat, maskB, 1, daBin, NULL, rfBin); 303 304 // XXX EAM : this is the intended API (cycle 7? cycle 8?) 305 // poly = psVectorFitPolynomial1D(poly, maskB, 1, daBin, NULL, rfBin); 306 307 // XXX EAM : replace this when the above version is implemented 308 // poly = psVectorFitPolynomial1DOrd(poly, maskB, rfBin, daBin, NULL); 309 310 psVector *daBinFit = psPolynomial1DEvalVector (poly, rfBin); 311 psVector *daResid = (psVector *) psBinaryOp (NULL, (void *) daBin, "-", (void *) daBinFit); 312 313 stats = psStatsAlloc (PS_STAT_CLIPPED_STDEV); 314 stats = psVectorStats (stats, daResid, NULL, maskB, 1); 315 316 psfTry->psf->ApResid = poly->coeff[0]; 317 psfTry->psf->dApResid = stats->clippedStdev; 318 psfTry->psf->skyBias = poly->coeff[1] / (M_PI * PS_SQR(RADIUS)); 319 320 psFree (rflux); 321 psFree (rfBin); 322 psFree (daBin); 323 psFree (maskB); 324 psFree (daBinFit); 325 psFree (daResid); 326 psFree (poly); 327 psFree (stats); 328 psFree (fitstat); 329 330 return true; 331 } 332 333 bool pmPSFtryMetric_Alt (pmPSFtry *try 334 , float RADIUS) 335 { 336 337 // the measured (aperture - fit) magnitudes (dA == try->metric) 338 // depend on both the true ap-fit (dAo) and the bias in the sky measurement: 339 // dA = dAo + dsky/flux 340 // where flux is the flux of the star 341 // we fit this trend to find the infinite flux aperture correction (dAo), 342 // the nominal sky bias (dsky), and the error on dAo 343 // the values of dA are contaminated by stars with close neighbors in the aperture 344 // we use an outlier rejection to avoid this bias 345 346 // rflux = ten(0.4*fitMag); 347 psVector *rflux = psVectorAlloc (try 348 ->sources->n, PS_TYPE_F64); 349 rflux->n = rflux->nalloc; 350 for (int i = 0; i < try 351 ->sources->n; i++) { 352 if (try 353 ->mask->data.U8[i] & PSFTRY_MASK_ALL) continue; 354 rflux->data.F64[i] = pow(10.0, 0.4*try 355 ->fitMag->data.F64[i]); 356 } 357 358 // XXX EAM : try 3hi/1lo sigma clipping on the rflux vs metric fit 243 r2rflux->data.F64[i] = PS_SQR(RADIUS) * pow(10.0, 0.4*psfTry->fitMag->data.F64[i]); 244 } 245 246 // use 3hi/1lo sigma clipping on the r2rflux vs metric fit 359 247 psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV); 360 361 // XXX EAM362 248 stats->min = 1.0; 363 249 stats->max = 3.0; 364 250 stats->clipIter = 3; 365 251 366 // linear clipped fit to rfBin, daBin 367 psPolynomial1D *poly = psPolynomial1DAlloc (1, PS_POLYNOMIAL_ORD); 368 poly = psVectorClipFitPolynomial1D (poly, stats, try 369 ->mask, PSFTRY_MASK_ALL, try 370 ->metric, NULL, rflux); 371 fprintf (stderr, "fit stats: %f +/- %f\n", stats->sampleMedian, stats->sampleStdev); 372 373 try 374 ->psf->ApResid = poly->coeff[0]; 375 try 376 ->psf->dApResid = stats->sampleStdev; 377 try 378 ->psf->skyBias = poly->coeff[1] / (M_PI * PS_SQR(RADIUS)); 379 380 fprintf (stderr, "*******************************************************************************\n"); 381 382 FILE *f; 383 f = fopen ("apresid.dat", "w"); 384 if (f == NULL) 385 psAbort ("pmPSFtry", "can't open output file"); 386 387 for (int i = 0; i < try 388 ->sources->n; i++) { 389 fprintf (f, "%3d %8.4f %12.5e %8.4f %3d\n", i, try 390 ->fitMag->data.F64[i], rflux->data.F64[i], try 391 ->metric->data.F64[i], try 392 ->mask->data.U8[i]); 393 } 394 fclose (f); 395 396 psFree (rflux); 252 // fit ApTrend only to r2rflux, ignore x,y,flux variations for now 253 // linear clipped fit of ApResid to r2rflux 254 psPolynomial1D *poly = psPolynomial1DAlloc (PS_POLYNOMIAL_ORD, 1); 255 poly = psVectorClipFitPolynomial1D (poly, stats, psfTry->mask, PSFTRY_MASK_ALL, psfTry->metric, NULL, r2rflux); 256 psLogMsg ("pmPSFtryMetric", 4, "fit stats: %f +/- %f\n", stats->sampleMedian, stats->sampleStdev); 257 258 pmPSF_MaskApTrend (psfTry->psf, PM_PSF_SKYBIAS); 259 psfTry->psf->ApTrend->coeff[0][0][0][0] = poly->coeff[0]; 260 psfTry->psf->ApTrend->coeff[0][0][1][0] = 0; 261 262 psfTry->psf->skyBias = poly->coeff[1]; 263 psfTry->psf->ApResid = poly->coeff[0]; 264 psfTry->psf->dApResid = stats->sampleStdev; 265 266 psFree (r2rflux); 397 267 psFree (poly); 398 268 psFree (stats); 399 269 400 // psFree (daFit);401 // psFree (daResid);402 403 270 return true; 404 271 } 272 273 /* 274 (aprMag' - fitMag) = rflux*skyBias + ApTrend(x,y) 275 (aprMag - rflux*skyBias) - fitMag = ApTrend(x,y) 276 (aprMag - rflux*skyBias) = fitMag + ApTrend(x,y) 277 */ 278 -
trunk/psModules/src/objects/pmPSFtry.h
r6872 r6873 6 6 * @author EAM, IfA 7 7 * 8 * @version $Revision: 1.4 $ $Name: not supported by cvs2svn $9 * @date $Date: 2006-04-17 18:01:05 $10 8 * 11 9 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
Note:
See TracChangeset
for help on using the changeset viewer.
