Changeset 17443
- Timestamp:
- Apr 12, 2008, 10:06:01 PM (18 years ago)
- Location:
- trunk/psphot/src
- Files:
-
- 11 added
- 4 edited
-
Makefile.am (modified) (2 diffs)
-
pmDetections.c (added)
-
pmFootprint.c (modified) (4 diffs)
-
pmFootprint.h (modified) (1 diff)
-
pmFootprintArrayGrow.c (added)
-
pmFootprintArraysMerge.c (added)
-
pmFootprintIDs.c (added)
-
pmFootprintsAssignPeaks.c (added)
-
pmFootprintsFind.c (added)
-
pmFootprintsFindAtPoint.c (added)
-
pmSpan.c (added)
-
psphot.h (modified) (1 diff)
-
psphotCullPeaks.c (added)
-
psphotFindFootprints.c (added)
-
psphotSignificanceImage.c (added)
Legend:
- Unmodified
- Added
- Removed
-
trunk/psphot/src/Makefile.am
r17396 r17443 20 20 psphotErrorCodes.c \ 21 21 pmFootprint.c \ 22 pmFootprintArrayGrow.c \ 23 pmFootprintArraysMerge.c \ 24 pmFootprintIDs.c \ 25 pmFootprintsFind.c \ 26 pmFootprintsFindAtPoint.c \ 27 pmFootprintsAssignPeaks.c \ 28 pmDetections.c \ 29 pmSpan.c \ 30 psphotCullPeaks.c \ 22 31 psphotVersion.c \ 23 32 psphotModelGroupInit.c \ … … 29 38 psphotFindDetections.c \ 30 39 psphotFindPeaks.c \ 40 psphotFindFootprints.c \ 41 psphotSignificanceImage.c \ 31 42 psphotSourceStats.c \ 32 43 psphotRoughClass.c \ -
trunk/psphot/src/pmFootprint.c
r16820 r17443 1 /*****************************************************************************/ 2 /* 3 * Handle pmFootprints 4 */ 5 #include <assert.h> 6 #include <string.h> 7 #include <pslib.h> 8 #include <psmodules.h> 9 #include "psphot.h" 10 #include "pmFootprint.h" 1 # include "psphotInternal.h" 11 2 12 static void spanFree(pmSpan *tmp) { 13 ; 14 } 15 16 /* 17 * pmSpanAlloc() 18 */ 19 pmSpan *pmSpanAlloc(int y, int x0, int x1) 20 { 21 pmSpan *span = (pmSpan *)psAlloc(sizeof(pmSpan)); 22 psMemSetDeallocator(span, (psFreeFunc) spanFree); 23 24 span->y = y; 25 span->x0 = x0; 26 span->x1 = x1; 27 28 return(span); 29 } 30 31 bool pmSpanTest(const psPtr ptr) 32 { 33 return (psMemGetDeallocator(ptr) == (psFreeFunc)spanFree); 34 } 35 36 // 37 // Sort pmSpans by y, then x0, then x1 38 // 39 int pmSpanSortByYX(const void **a, const void **b) { 40 const pmSpan *sa = *(const pmSpan **)a; 41 const pmSpan *sb = *(const pmSpan **)b; 42 43 if (sa->y < sb->y) { 44 return -1; 45 } else if (sa->y == sb->y) { 46 if (sa->x0 < sb->x0) { 47 return -1; 48 } else if (sa->x0 == sb->x0) { 49 if (sa->x1 < sb->x1) { 50 return -1; 51 } else if (sa->x1 == sb->x1) { 52 return 0; 53 } else { 54 return 1; 55 } 56 } else { 57 return 1; 58 } 59 } else { 60 return 1; 61 } 62 } 63 64 /************************************************************************************************************/ 3 /**** low-level pmFootprint functions ****/ 65 4 66 5 static void footprintFree(pmFootprint *tmp) … … 131 70 // Add a span to a footprint, returning the new span 132 71 // 133 staticpmSpan *pmFootprintAddSpan(pmFootprint *fp, // the footprint to add to134 const int y, // row to add135 int x0, // range of136 int x1) { // columns72 pmSpan *pmFootprintAddSpan(pmFootprint *fp, // the footprint to add to 73 const int y, // row to add 74 int x0, // range of 75 int x1) { // columns 137 76 138 77 if (x1 < x0) { … … 201 140 } 202 141 203 /************************************************************************************************************/204 205 typedef struct { /* run-length code for part of object*/206 int id; /* ID for object */207 int y; /* Row wherein WSPAN dwells */208 int x0, x1; /* inclusive range of columns */209 } WSPAN;210 211 /*212 * comparison function for qsort; sort by ID then row213 */214 static int215 compar(const void *va, const void *vb)216 {217 const WSPAN *a = va;218 const WSPAN *b = vb;219 220 if(a->id < b->id) {221 return(-1);222 } else if(a->id > b->id) {223 return(1);224 } else {225 return(a->y - b->y);226 }227 }228 229 /*230 * Follow a chain of aliases, returning the final resolved value.231 */232 static int233 resolve_alias(const int *aliases, /* list of aliases */234 int id) /* alias to look up */235 {236 int resolved = id; /* resolved alias */237 238 while(id != aliases[id]) {239 resolved = id = aliases[id];240 }241 242 return(resolved);243 }244 245 /*246 * Go through an image, finding sets of connected pixels above threshold247 * and assembling them into pmFootprints; the resulting set of objects248 * is returned as a psArray249 */250 psArray *251 pmFindFootprints(const psImage *img, // image to search252 const float threshold, // Threshold253 const int npixMin) // minimum number of pixels in an acceptable pmFootprint254 {255 int i0; /* initial value of i */256 int id; /* object ID */257 int in_span; /* object ID of current WSPAN */258 int nspan = 0; /* number of spans */259 int nobj = 0; /* number of objects found */260 int x0 = 0; /* unpacked from a WSPAN */261 int *tmp; /* used in swapping idc/idp */262 263 assert(img != NULL);264 265 bool F32 = false; // is this an F32 image?266 if (img->type.type == PS_TYPE_F32) {267 F32 = true;268 } else if (img->type.type == PS_TYPE_S32) {269 F32 = false;270 } else { // N.b. You can't trivially add more cases here; F32 is just a bool271 psError(PS_ERR_UNKNOWN, true, "Unsupported psImage type: %d", img->type.type);272 return NULL;273 }274 psF32 *imgRowF32 = NULL; // row pointer if F32275 psS32 *imgRowS32 = NULL; // " " " " !F32276 277 const int row0 = img->row0;278 const int col0 = img->col0;279 const int numRows = img->numRows;280 const int numCols = img->numCols;281 /*282 * Storage for arrays that identify objects by ID. We want to be able to283 * refer to idp[-1] and idp[numCols], hence the (numCols + 2)284 */285 int *id_s = psAlloc(2*(numCols + 2)*sizeof(int));286 memset(id_s, '\0', 2*(numCols + 2)*sizeof(int)); assert(id_s[0] == 0);287 int *idc = id_s + 1; // object IDs in current/288 int *idp = idc + (numCols + 2); // previous row289 290 int size_aliases = 1 + numRows/20; // size of aliases[] array291 int *aliases = psAlloc(size_aliases*sizeof(int)); // aliases for object IDs292 293 int size_spans = 1 + numRows/20; // size of spans[] array294 WSPAN *spans = psAlloc(size_spans*sizeof(WSPAN)); // row:x0,x1 for objects295 /*296 * Go through image identifying objects297 */298 for (int i = 0; i < numRows; i++) {299 int j;300 tmp = idc; idc = idp; idp = tmp; /* swap ID pointers */301 memset(idc, '\0', numCols*sizeof(int));302 303 imgRowF32 = img->data.F32[i]; // only one of304 imgRowS32 = img->data.S32[i]; // these is valid!305 306 in_span = 0; /* not in a span */307 for (j = 0; j < numCols; j++) {308 double pixVal = F32 ? imgRowF32[j] : imgRowS32[j];309 if (pixVal < threshold) {310 if (in_span) {311 if(nspan >= size_spans) {312 size_spans *= 2;313 spans = psRealloc(spans, size_spans*sizeof(WSPAN));314 }315 spans[nspan].id = in_span;316 spans[nspan].y = i;317 spans[nspan].x0 = x0;318 spans[nspan].x1 = j - 1;319 320 nspan++;321 322 in_span = 0;323 }324 } else { /* a pixel to fix */325 if(idc[j - 1] != 0) {326 id = idc[j - 1];327 } else if(idp[j - 1] != 0) {328 id = idp[j - 1];329 } else if(idp[j] != 0) {330 id = idp[j];331 } else if(idp[j + 1] != 0) {332 id = idp[j + 1];333 } else {334 id = ++nobj;335 336 if(id >= size_aliases) {337 size_aliases *= 2;338 aliases = psRealloc(aliases, size_aliases*sizeof(int));339 }340 aliases[id] = id;341 }342 343 idc[j] = id;344 if(!in_span) {345 x0 = j; in_span = id;346 }347 /*348 * Do we need to merge ID numbers? If so, make suitable entries in aliases[]349 */350 if(idp[j + 1] != 0 && idp[j + 1] != id) {351 aliases[resolve_alias(aliases, idp[j + 1])] =352 resolve_alias(aliases, id);353 354 idc[j] = id = idp[j + 1];355 }356 }357 }358 359 if(in_span) {360 if(nspan >= size_spans) {361 size_spans *= 2;362 spans = psRealloc(spans, size_spans*sizeof(WSPAN));363 }364 365 assert(nspan < size_spans); /* we checked for space above */366 spans[nspan].id = in_span;367 spans[nspan].y = i;368 spans[nspan].x0 = x0;369 spans[nspan].x1 = j - 1;370 371 nspan++;372 }373 }374 375 psFree(id_s);376 /*377 * Resolve aliases; first alias chains, then the IDs in the spans378 */379 for (int i = 0; i < nspan; i++) {380 spans[i].id = resolve_alias(aliases, spans[i].id);381 }382 383 psFree(aliases);384 /*385 * Sort spans by ID, so we can sweep through them once386 */387 if(nspan > 0) {388 qsort(spans, nspan, sizeof(WSPAN), compar);389 }390 /*391 * Build pmFootprints from the spans392 */393 psArray *footprints = psArrayAlloc(nobj);394 int n = 0; // number of pmFootprints395 396 if(nspan > 0) {397 id = spans[0].id;398 i0 = 0;399 for (int i = 0; i <= nspan; i++) { /* nspan + 1 to catch last object */400 if(i == nspan || spans[i].id != id) {401 pmFootprint *fp = pmFootprintAlloc(i - i0, img);402 403 for(; i0 < i; i0++) {404 pmFootprintAddSpan(fp, spans[i0].y + row0,405 spans[i0].x0 + col0, spans[i0].x1 + col0);406 }407 408 if (fp->npix < npixMin) {409 psFree(fp);410 } else {411 footprints->data[n++] = fp;412 }413 }414 415 id = spans[i].id;416 }417 }418 419 footprints = psArrayRealloc(footprints, n);420 /*421 * clean up422 */423 psFree(spans);424 425 return footprints;426 }427 428 /************************************************************************************************************/429 /*430 * A data structure to hold the starting point for a search for pixels above threshold,431 * used by pmFindFootprintAtPoint432 *433 * We don't want to find this span again --- it's already part of the footprint ---434 * so we set appropriate mask bits435 */436 //437 // An enum for what we should do with a Startspan438 //439 typedef enum {PM_SSPAN_DOWN = 0, // scan down from this span440 PM_SSPAN_UP, // scan up from this span441 PM_SSPAN_RESTART, // restart scanning from this span442 PM_SSPAN_DONE // this span is processed443 } PM_SSPAN_DIR; // How to continue searching444 //445 // An enum for mask's pixel values. We're looking for pixels that are above threshold, and446 // we keep extra book-keeping information in the PM_SSPAN_STOP plane. It's simpler to be447 // able to check for448 //449 enum {450 PM_SSPAN_INITIAL = 0x0, // initial state of pixels.451 PM_SSPAN_DETECTED = 0x1, // we've seen this pixel452 PM_SSPAN_STOP = 0x2 // you may stop searching when you see this pixel453 };454 //455 // The struct that remembers how to [re-]start scanning the image for pixels456 //457 typedef struct {458 const pmSpan *span; // save the pixel range459 PM_SSPAN_DIR direction; // How to continue searching460 bool stop; // should we stop searching?461 } Spartspan;462 463 static void startspanFree(Spartspan *sspan) {464 psFree((void *)sspan->span);465 }466 467 static Spartspan *468 SpartspanAlloc(const pmSpan *span, // The span in question469 psImage *mask, // Pixels that we've already detected470 const PM_SSPAN_DIR dir // Should we continue searching towards the top of the image?471 ) {472 Spartspan *sspan = psAlloc(sizeof(Spartspan));473 psMemSetDeallocator(sspan, (psFreeFunc)startspanFree);474 475 sspan->span = psMemIncrRefCounter((void *)span);476 sspan->direction = dir;477 sspan->stop = false;478 479 if (mask != NULL) { // remember that we've detected these pixels480 psMaskType *mpix = &mask->data.PS_TYPE_MASK_DATA[span->y - mask->row0][span->x0 - mask->col0];481 482 for (int i = 0; i <= span->x1 - span->x0; i++) {483 mpix[i] |= PM_SSPAN_DETECTED;484 if (mpix[i] & PM_SSPAN_STOP) {485 sspan->stop = true;486 }487 }488 }489 490 return sspan;491 }492 493 //494 // Add a new Spartspan to an array of Spartspans. Iff we see a stop bit, return true495 //496 static bool add_startspan(psArray *startspans, // the saved Spartspans497 const pmSpan *sp, // the span in question498 psImage *mask, // mask of detected/stop pixels499 const PM_SSPAN_DIR dir) { // the desired direction to search500 if (dir == PM_SSPAN_RESTART) {501 if (add_startspan(startspans, sp, mask, PM_SSPAN_UP) ||502 add_startspan(startspans, sp, NULL, PM_SSPAN_DOWN)) {503 return true;504 }505 } else {506 Spartspan *sspan = SpartspanAlloc(sp, mask, dir);507 if (sspan->stop) { // we detected a stop bit508 psFree(sspan); // don't allocate new span509 510 return true;511 } else {512 psArrayAdd(startspans, 1, sspan);513 psFree(sspan); // as it's now owned by startspans514 }515 }516 517 return false;518 }519 520 /************************************************************************************************************/521 /*522 * Search the image for pixels above threshold, starting at a single Spartspan.523 * We search the array looking for one to process; it'd be better to move the524 * ones that we're done with to the end, but it probably isn't worth it for525 * the anticipated uses of this routine.526 *527 * This is the guts of pmFindFootprintAtPoint528 */529 static bool do_startspan(pmFootprint *fp, // the footprint that we're building530 const psImage *img, // the psImage we're working on531 psImage *mask, // the associated masks532 const float threshold, // Threshold533 psArray *startspans) { // specify which span to process next534 bool F32 = false; // is this an F32 image?535 if (img->type.type == PS_TYPE_F32) {536 F32 = true;537 } else if (img->type.type == PS_TYPE_S32) {538 F32 = false;539 } else { // N.b. You can't trivially add more cases here; F32 is just a bool540 psError(PS_ERR_UNKNOWN, true, "Unsupported psImage type: %d", img->type.type);541 return NULL;542 }543 544 psF32 *imgRowF32 = NULL; // row pointer if F32545 psS32 *imgRowS32 = NULL; // " " " " !F32546 psMaskType *maskRow = NULL; // masks's row pointer547 548 const int row0 = img->row0;549 const int col0 = img->col0;550 const int numRows = img->numRows;551 const int numCols = img->numCols;552 553 /********************************************************************************************************/554 555 Spartspan *sspan = NULL;556 for (int i = 0; i < startspans->n; i++) {557 sspan = startspans->data[i];558 if (sspan->direction != PM_SSPAN_DONE) {559 break;560 }561 if (sspan->stop) {562 break;563 }564 }565 if (sspan == NULL || sspan->direction == PM_SSPAN_DONE) { // no more Spartspans to process566 return false;567 }568 if (sspan->stop) { // they don't want any more spans processed569 return false;570 }571 /*572 * Work573 */574 const PM_SSPAN_DIR dir = sspan->direction;575 /*576 * Set initial span to the startspan577 */578 int x0 = sspan->span->x0 - col0, x1 = sspan->span->x1 - col0;579 /*580 * Go through image identifying objects581 */582 int nx0, nx1 = -1; // new values of x0, x1583 const int di = (dir == PM_SSPAN_UP) ? 1 : -1; // how much i changes to get to the next row584 bool stop = false; // should I stop searching for spans?585 586 for (int i = sspan->span->y -row0 + di; i < numRows && i >= 0; i += di) {587 imgRowF32 = img->data.F32[i]; // only one of588 imgRowS32 = img->data.S32[i]; // these is valid!589 maskRow = mask->data.PS_TYPE_MASK_DATA[i];590 //591 // Search left from the pixel diagonally to the left of (i - di, x0). If there's592 // a connected span there it may need to grow up and/or down, so push it onto593 // the stack for later consideration594 //595 nx0 = -1;596 for (int j = x0 - 1; j >= -1; j--) {597 double pixVal = (j < 0) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);598 if ((maskRow[j] & PM_SSPAN_DETECTED) || pixVal < threshold) {599 if (j < x0 - 1) { // we found some pixels above threshold600 nx0 = j + 1;601 }602 break;603 }604 }605 606 if (nx0 < 0) { // no span to the left607 nx1 = x0 - 1; // we're going to resume searching at nx1 + 1608 } else {609 //610 // Search right in leftmost span611 //612 //nx1 = 0; // make gcc happy613 for (int j = nx0 + 1; j <= numCols; j++) {614 double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);615 if ((maskRow[j] & PM_SSPAN_DETECTED) || pixVal < threshold) {616 nx1 = j - 1;617 break;618 }619 }620 621 const pmSpan *sp = pmFootprintAddSpan(fp, i + row0, nx0 + col0, nx1 + col0);622 623 if (add_startspan(startspans, sp, mask, PM_SSPAN_RESTART)) {624 stop = true;625 break;626 }627 }628 //629 // Now look for spans connected to the old span. The first of these we'll630 // simply process, but others will have to be deferred for later consideration.631 //632 // In fact, if the span overhangs to the right we'll have to defer the overhang633 // until later too, as it too can grow in both directions634 //635 // Note that column numCols exists virtually, and always ends the last span; this636 // is why we claim below that sx1 is always set637 //638 bool first = false; // is this the first new span detected?639 for (int j = nx1 + 1; j <= x1 + 1; j++) {640 double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);641 if (!(maskRow[j] & PM_SSPAN_DETECTED) && pixVal >= threshold) {642 int sx0 = j++; // span that we're working on is sx0:sx1643 int sx1 = -1; // We know that if we got here, we'll also set sx1644 for (; j <= numCols; j++) {645 double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);646 if ((maskRow[j] & PM_SSPAN_DETECTED) || pixVal < threshold) { // end of span647 sx1 = j;648 break;649 }650 }651 assert (sx1 >= 0);652 653 const pmSpan *sp;654 if (first) {655 if (sx1 <= x1) {656 sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, sx1 + col0 - 1);657 if (add_startspan(startspans, sp, mask, PM_SSPAN_DONE)) {658 stop = true;659 break;660 }661 } else { // overhangs to right662 sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, x1 + col0);663 if (add_startspan(startspans, sp, mask, PM_SSPAN_DONE)) {664 stop = true;665 break;666 }667 sp = pmFootprintAddSpan(fp, i + row0, x1 + 1 + col0, sx1 + col0 - 1);668 if (add_startspan(startspans, sp, mask, PM_SSPAN_RESTART)) {669 stop = true;670 break;671 }672 }673 first = false;674 } else {675 sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, sx1 + col0 - 1);676 if (add_startspan(startspans, sp, mask, PM_SSPAN_RESTART)) {677 stop = true;678 break;679 }680 }681 }682 }683 684 if (stop || first == false) { // we're done685 break;686 }687 688 x0 = nx0; x1 = nx1;689 }690 /*691 * Cleanup692 */693 694 sspan->direction = PM_SSPAN_DONE;695 return stop ? false : true;696 }697 698 /*699 * Go through an image, starting at (row, col) and assembling all the pixels700 * that are connected to that point (in a chess kings-move sort of way) into701 * a pmFootprint.702 *703 * This is much slower than pmFindFootprints if you want to find lots of704 * footprints, but if you only want a small region about a given point it705 * can be much faster706 *707 * N.b. The returned pmFootprint is not in "normal form"; that is the pmSpans708 * are not sorted by increasing y, x0, x1. If this matters to you, call709 * pmFootprintNormalize()710 */711 pmFootprint *712 pmFindFootprintAtPoint(const psImage *img, // image to search713 const float threshold, // Threshold714 const psArray *peaks, // array of peaks; finding one terminates search for footprint715 int row, int col) { // starting position (in img's parent's coordinate system)716 assert(img != NULL);717 718 bool F32 = false; // is this an F32 image?719 if (img->type.type == PS_TYPE_F32) {720 F32 = true;721 } else if (img->type.type == PS_TYPE_S32) {722 F32 = false;723 } else { // N.b. You can't trivially add more cases here; F32 is just a bool724 psError(PS_ERR_UNKNOWN, true, "Unsupported psImage type: %d", img->type.type);725 return NULL;726 }727 psF32 *imgRowF32 = NULL; // row pointer if F32728 psS32 *imgRowS32 = NULL; // " " " " !F32729 730 const int row0 = img->row0;731 const int col0 = img->col0;732 const int numRows = img->numRows;733 const int numCols = img->numCols;734 /*735 * Is point in image, and above threshold?736 */737 row -= row0; col -= col0;738 if (row < 0 || row >= numRows ||739 col < 0 || col >= numCols) {740 psError(PS_ERR_BAD_PARAMETER_VALUE, true,741 "row/col == (%d, %d) are out of bounds [%d--%d, %d--%d]",742 row + row0, col + col0, row0, row0 + numRows - 1, col0, col0 + numCols - 1);743 return NULL;744 }745 746 double pixVal = F32 ? img->data.F32[row][col] : img->data.S32[row][col];747 if (pixVal < threshold) {748 return pmFootprintAlloc(0, img);749 }750 751 pmFootprint *fp = pmFootprintAlloc(1 + img->numRows/10, img);752 /*753 * We need a mask for two purposes; to indicate which pixels are already detected,754 * and to store the "stop" pixels --- those that, once reached, should stop us755 * looking for the rest of the pmFootprint. These are generally set from peaks.756 */757 psImage *mask = psImageAlloc(numCols, numRows, PS_TYPE_MASK);758 P_PSIMAGE_SET_ROW0(mask, row0);759 P_PSIMAGE_SET_COL0(mask, col0);760 psImageInit(mask, PM_SSPAN_INITIAL);761 //762 // Set stop bits from peaks list763 //764 assert (peaks == NULL || peaks->n == 0 || psMemCheckPeak(peaks->data[0]));765 if (peaks != NULL) {766 for (int i = 0; i < peaks->n; i++) {767 pmPeak *peak = peaks->data[i];768 mask->data.PS_TYPE_MASK_DATA[peak->y - mask->row0][peak->x - mask->col0] |= PM_SSPAN_STOP;769 }770 }771 /*772 * Find starting span passing through (row, col)773 */774 psArray *startspans = psArrayAllocEmpty(1); // spans where we have to restart the search775 776 imgRowF32 = img->data.F32[row]; // only one of777 imgRowS32 = img->data.S32[row]; // these is valid!778 psMaskType *maskRow = mask->data.PS_TYPE_MASK_DATA[row];779 {780 int i;781 for (i = col; i >= 0; i--) {782 pixVal = F32 ? imgRowF32[i] : imgRowS32[i];783 if ((maskRow[i] & PM_SSPAN_DETECTED) || pixVal < threshold) {784 break;785 }786 }787 int i0 = i;788 for (i = col; i < numCols; i++) {789 pixVal = F32 ? imgRowF32[i] : imgRowS32[i];790 if ((maskRow[i] & PM_SSPAN_DETECTED) || pixVal < threshold) {791 break;792 }793 }794 int i1 = i;795 const pmSpan *sp = pmFootprintAddSpan(fp, row + row0, i0 + col0 + 1, i1 + col0 - 1);796 797 (void)add_startspan(startspans, sp, mask, PM_SSPAN_RESTART);798 }799 /*800 * Now workout from those Spartspans, searching for pixels above threshold801 */802 while (do_startspan(fp, img, mask, threshold, startspans)) continue;803 /*804 * Cleanup805 */806 psFree(mask);807 psFree(startspans); // restores the image pixel808 809 return fp; // pmFootprint really810 }811 812 /************************************************************************************************************/813 /*814 * Worker routine for the pmSetFootprintArrayIDs/pmSetFootprintID (and pmMergeFootprintArrays)815 */816 static void817 set_footprint_id(psImage *idImage, // the image to set818 const pmFootprint *fp, // the footprint to insert819 const int id) { // the desired ID820 const int col0 = fp->region.x0;821 const int row0 = fp->region.y0;822 823 for (int j = 0; j < fp->spans->n; j++) {824 const pmSpan *span = fp->spans->data[j];825 psS32 *imgRow = idImage->data.S32[span->y - row0];826 for(int k = span->x0 - col0; k <= span->x1 - col0; k++) {827 imgRow[k] += id;828 }829 }830 }831 832 static void833 set_footprint_array_ids(psImage *idImage,834 const psArray *footprints, // the footprints to insert835 const bool relativeIDs) { // show IDs starting at 0, not pmFootprint->id836 int id = 0; // first index will be 1837 for (int i = 0; i < footprints->n; i++) {838 const pmFootprint *fp = footprints->data[i];839 if (relativeIDs) {840 id++;841 } else {842 id = fp->id;843 }844 845 set_footprint_id(idImage, fp, id);846 }847 }848 849 /*850 * Set an image to the value of footprint's ID whever they may fall851 */852 psImage *pmSetFootprintArrayIDs(const psArray *footprints, // the footprints to insert853 const bool relativeIDs) { // show IDs starting at 1, not pmFootprint->id854 assert (footprints != NULL);855 856 if (footprints->n == 0) {857 psError(PS_ERR_BAD_PARAMETER_SIZE, true, "You didn't provide any footprints");858 return NULL;859 }860 const pmFootprint *fp = footprints->data[0];861 assert(pmFootprintTest((const psPtr)fp));862 const int numCols = fp->region.x1 - fp->region.x0 + 1;863 const int numRows = fp->region.y1 - fp->region.y0 + 1;864 const int col0 = fp->region.x0;865 const int row0 = fp->region.y0;866 assert (numCols >= 0 && numRows >= 0);867 868 psImage *idImage = psImageAlloc(numCols, numRows, PS_TYPE_S32);869 P_PSIMAGE_SET_ROW0(idImage, row0);870 P_PSIMAGE_SET_COL0(idImage, col0);871 psImageInit(idImage, 0);872 /*873 * do the work874 */875 set_footprint_array_ids(idImage, footprints, relativeIDs);876 877 return idImage;878 879 }880 881 /*882 * Set an image to the value of footprint's ID whever they may fall883 */884 psImage *pmSetFootprintID(const pmFootprint *fp, // the footprint to insert885 const int id) { // the desired ID886 assert(fp != NULL && pmFootprintTest((const psPtr)fp));887 const int numCols = fp->region.x1 - fp->region.x0 + 1;888 const int numRows = fp->region.y1 - fp->region.y0 + 1;889 const int col0 = fp->region.x0;890 const int row0 = fp->region.y0;891 assert (numCols >= 0 && numRows >= 0);892 893 psImage *idImage = psImageAlloc(numCols, numRows, PS_TYPE_S32);894 P_PSIMAGE_SET_ROW0(idImage, row0);895 P_PSIMAGE_SET_COL0(idImage, col0);896 psImageInit(idImage, 0);897 /*898 * do the work899 */900 set_footprint_id(idImage, fp, id);901 902 return idImage;903 904 }905 906 /************************************************************************************************************/907 /*908 * Grow a psArray of pmFootprints isotropically by r pixels, returning a new psArray of new pmFootprints909 */910 psArray *pmGrowFootprintArray(const psArray *footprints, // footprints to grow911 int r) { // how much to grow each footprint912 assert (footprints->n == 0 || pmFootprintTest(footprints->data[0]));913 914 if (footprints->n == 0) { // we don't know the size of the footprint's region915 return psArrayAlloc(0);916 }917 /*918 * We'll insert the footprints into an image, then convolve with a disk,919 * then extract a footprint from the result --- this is magically what we want.920 */921 psImage *idImage = pmSetFootprintArrayIDs(footprints, true);922 if (r <= 0) {923 r = 1; // r == 1 => no grow924 }925 psKernel *circle = psKernelAlloc(-r, r, -r, r);926 assert (circle->image->numRows == 2*r + 1 && circle->image->numCols == circle->image->numRows);927 for (int i = 0; i <= r; i++) {928 for (int j = 0; j <= r; j++) {929 if (i*i + j*j <= r*r) {930 circle->kernel[i][j] =931 circle->kernel[i][-j] =932 circle->kernel[-i][j] =933 circle->kernel[-i][-j] = 1;934 }935 }936 }937 938 psImage *grownIdImage = psImageConvolveDirect(NULL, idImage, circle); // Here's the actual grow step939 psFree(circle);940 941 psArray *grown = pmFindFootprints(grownIdImage, 0.5, 1); // and here we rebuild the grown footprints942 assert (grown != NULL);943 psFree(idImage); psFree(grownIdImage);944 /*945 * Now assign the peaks appropriately. We could do this more efficiently946 * using grownIdImage (which we just freed), but this is easy and probably fast enough947 */948 const psArray *peaks = pmFootprintArrayToPeaks(footprints);949 pmPeaksAssignToFootprints(grown, peaks);950 psFree((psArray *)peaks);951 952 return grown;953 954 }955 956 /************************************************************************************************************/957 /*958 * Merge together two psArrays of pmFootprints neither of which is damaged.959 *960 * The returned psArray may contain elements of the inital psArrays (with961 * their reference counters suitable incremented)962 */963 psArray *pmMergeFootprintArrays(const psArray *footprints1, // one set of footprints964 const psArray *footprints2, // the other set965 const int includePeaks) { // which peaks to set? 0x1 => footprints1, 0x2 => 2966 assert (footprints1->n == 0 || pmFootprintTest(footprints1->data[0]));967 assert (footprints2->n == 0 || pmFootprintTest(footprints2->data[0]));968 969 if (footprints1->n == 0 || footprints2->n == 0) { // nothing to do but put copies on merged970 const psArray *old = (footprints1->n == 0) ? footprints2 : footprints1;971 972 psArray *merged = psArrayAllocEmpty(old->n);973 for (int i = 0; i < old->n; i++) {974 psArrayAdd(merged, 1, old->data[i]);975 }976 977 return merged;978 }979 /*980 * We have real work to do as some pmFootprints in footprints2 may overlap981 * with footprints1982 */983 {984 pmFootprint *fp1 = footprints1->data[0];985 pmFootprint *fp2 = footprints2->data[0];986 if (fp1->region.x0 != fp2->region.x0 ||987 fp1->region.x1 != fp2->region.x1 ||988 fp1->region.y0 != fp2->region.y0 ||989 fp1->region.y1 != fp2->region.y1) {990 psError(PS_ERR_BAD_PARAMETER_SIZE, true,991 "The two pmFootprint arrays correspnond to different-sized regions");992 return NULL;993 }994 }995 /*996 * We'll insert first one set of footprints then the other into an image, then997 * extract a footprint from the result --- this is magically what we want.998 */999 psImage *idImage = pmSetFootprintArrayIDs(footprints1, true);1000 set_footprint_array_ids(idImage, footprints2, true);1001 1002 psArray *merged = pmFindFootprints(idImage, 0.5, 1);1003 assert (merged != NULL);1004 psFree(idImage);1005 /*1006 * Now assign the peaks appropriately. We could do this more efficiently1007 * using idImage (which we just freed), but this is easy and probably fast enough1008 */1009 if (includePeaks & 0x1) {1010 const psArray *peaks = pmFootprintArrayToPeaks(footprints1);1011 pmPeaksAssignToFootprints(merged, peaks);1012 psFree((psArray *)peaks);1013 }1014 1015 if (includePeaks & 0x2) {1016 const psArray *peaks = pmFootprintArrayToPeaks(footprints2);1017 pmPeaksAssignToFootprints(merged, peaks);1018 psFree((psArray *)peaks);1019 }1020 1021 return merged;1022 }1023 1024 /************************************************************************************************************/1025 /*1026 * Given a psArray of pmFootprints and another of pmPeaks, assign the peaks to the1027 * footprints in which that fall; if they _don't_ fall in a footprint, add a suitable1028 * one to the list.1029 */1030 psErrorCode1031 pmPeaksAssignToFootprints(psArray *footprints, // the pmFootprints1032 const psArray *peaks) { // the pmPeaks1033 assert (footprints != NULL);1034 assert (footprints->n == 0 || pmFootprintTest(footprints->data[0]));1035 assert (peaks != NULL);1036 assert (peaks->n == 0 || psMemCheckPeak(peaks->data[0]));1037 1038 if (footprints->n == 0) {1039 if (peaks->n > 0) {1040 return psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Your list of footprints is empty");1041 }1042 return PS_ERR_NONE;1043 }1044 /*1045 * Create an image filled with the object IDs, and use it to assign pmPeaks to the1046 * objects1047 */1048 psImage *ids = pmSetFootprintArrayIDs(footprints, true);1049 assert (ids != NULL);1050 assert (ids->type.type == PS_TYPE_S32);1051 const int row0 = ids->row0;1052 const int col0 = ids->col0;1053 const int numRows = ids->numRows;1054 const int numCols = ids->numCols;1055 1056 for (int i = 0; i < peaks->n; i++) {1057 pmPeak *peak = peaks->data[i];1058 const int x = peak->x - col0;1059 const int y = peak->y - row0;1060 1061 assert (x >= 0 && x < numCols && y >= 0 && y < numRows);1062 int id = ids->data.S32[y][x - col0];1063 1064 if (id == 0) { // peak isn't in a footprint, so make one for it1065 pmFootprint *nfp = pmFootprintAlloc(1, ids);1066 pmFootprintAddSpan(nfp, y, x, x);1067 psArrayAdd(footprints, 1, nfp);1068 psFree(nfp);1069 id = footprints->n;1070 }1071 1072 assert (id >= 1 && id <= footprints->n);1073 pmFootprint *fp = footprints->data[id - 1];1074 psArrayAdd(fp->peaks, 5, peak);1075 }1076 1077 psFree(ids);1078 //1079 // Make sure that peaks within each footprint are sorted and unique1080 //1081 for (int i = 0; i < footprints->n; i++) {1082 pmFootprint *fp = footprints->data[i];1083 fp->peaks = psArraySort(fp->peaks, pmPeakSortBySN);1084 1085 for (int j = 1; j < fp->peaks->n; j++) { // check for duplicates1086 if (fp->peaks->data[j] == fp->peaks->data[j-1]) {1087 (void)psArrayRemoveIndex(fp->peaks, j);1088 j--; // we moved everything down one1089 }1090 }1091 }1092 1093 return PS_ERR_NONE;1094 }1095 1096 /************************************************************************************************************/1097 /*1098 * Examine the peaks in a pmFootprint, and throw away the ones that are not sufficiently1099 * isolated. More precisely, for each peak find the highest coll that you'd have to traverse1100 * to reach a still higher peak --- and if that coll's more than nsigma DN below your1101 * starting point, discard the peak.1102 */1103 psErrorCode pmFootprintCullPeaks(const psImage *img, // the image wherein lives the footprint1104 const psImage *weight, // corresponding variance image1105 pmFootprint *fp, // Footprint containing mortal peaks1106 const float nsigma_delta, // how many sigma above local background a peak1107 // needs to be to survive1108 const float min_threshold) { // minimum permitted coll height1109 assert (img != NULL); assert (img->type.type == PS_TYPE_F32);1110 assert (weight != NULL); assert (weight->type.type == PS_TYPE_F32);1111 assert (img->row0 == weight->row0 && img->col0 == weight->col0);1112 assert (fp != NULL);1113 1114 if (fp->peaks == NULL || fp->peaks->n == 0) { // nothing to do1115 return PS_ERR_NONE;1116 }1117 1118 psRegion subRegion; // desired subregion; 1 larger than bounding box (grr)1119 subRegion.x0 = fp->bbox.x0; subRegion.x1 = fp->bbox.x1 + 1;1120 subRegion.y0 = fp->bbox.y0; subRegion.y1 = fp->bbox.y1 + 1;1121 const psImage *subImg = psImageSubset((psImage *)img, subRegion);1122 const psImage *subWt = psImageSubset((psImage *)weight, subRegion);1123 assert (subImg != NULL && subWt != NULL);1124 //1125 // We need a psArray of peaks brighter than the current peak. We'll fake this1126 // by reusing the fp->peaks but lying about n.1127 //1128 // We do this for efficiency (otherwise I'd need two peaks lists), and we are1129 // rather too chummy with psArray in consequence. But it works.1130 //1131 psArray *brightPeaks = psArrayAlloc(0);1132 psFree(brightPeaks->data);1133 brightPeaks->data = psMemIncrRefCounter(fp->peaks->data);// use the data from fp->peaks1134 //1135 // The brightest peak is always safe; go through other peaks trying to cull them1136 //1137 for (int i = 1; i < fp->peaks->n; i++) { // n.b. fp->peaks->n can change within the loop1138 const pmPeak *peak = fp->peaks->data[i];1139 int x = peak->x - subImg->col0;1140 int y = peak->y - subImg->row0;1141 //1142 // Find the level nsigma below the peak that must separate the peak1143 // from any of its friends1144 //1145 assert (x >= 0 && x < subImg->numCols && y >= 0 && y < subImg->numRows);1146 const float stdev = sqrt(subWt->data.F32[y][x]);1147 float threshold = subImg->data.F32[y][x] - nsigma_delta*stdev;1148 if (isnan(threshold) || threshold < min_threshold) {1149 #if 1 // min_threshold is assumed to be below the detection threshold,1150 // so all the peaks are pmFootprint, and this isn't the brightest1151 (void)psArrayRemoveIndex(fp->peaks, i);1152 i--; // we moved everything down one1153 continue;1154 #else1155 #error n.b. We will be running LOTS of checks at this threshold, so only find the footprint once1156 threshold = min_threshold;1157 #endif1158 }1159 if (threshold > subImg->data.F32[y][x]) {1160 threshold = subImg->data.F32[y][x] - 10*FLT_EPSILON;1161 }1162 1163 const int peak_id = 1; // the ID for the peak of interest1164 brightPeaks->n = i; // only stop at a peak brighter than we are1165 pmFootprint *peakFootprint = pmFindFootprintAtPoint(subImg, threshold, brightPeaks, peak->y, peak->x);1166 brightPeaks->n = 0; // don't double free1167 psImage *idImg = pmSetFootprintID(peakFootprint, peak_id);1168 psFree(peakFootprint);1169 1170 int j;1171 for (j = 0; j < i; j++) {1172 const pmPeak *peak2 = fp->peaks->data[j];1173 int x2 = peak2->x - subImg->col0;1174 int y2 = peak2->y - subImg->row0;1175 const int peak2_id = idImg->data.S32[y2][x2]; // the ID for some other peak1176 1177 if (peak2_id == peak_id) { // There's a brighter peak within the footprint above1178 ; // threshold; so cull our initial peak1179 (void)psArrayRemoveIndex(fp->peaks, i);1180 i--; // we moved everything down one1181 break;1182 }1183 }1184 if (j == i) {1185 j++;1186 }1187 1188 psFree(idImg);1189 }1190 1191 brightPeaks->n = 0; psFree(brightPeaks);1192 psFree((psImage *)subImg);1193 psFree((psImage *)subWt);1194 1195 return PS_ERR_NONE;1196 }1197 1198 /*1199 * Cull an entire psArray of pmFootprints1200 */1201 psErrorCode1202 pmFootprintArrayCullPeaks(const psImage *img, // the image wherein lives the footprint1203 const psImage *weight, // corresponding variance image1204 psArray *footprints, // array of pmFootprints1205 const float nsigma_delta, // how many sigma above local background a peak1206 // needs to be to survive1207 const float min_threshold) { // minimum permitted coll height1208 for (int i = 0; i < footprints->n; i++) {1209 pmFootprint *fp = footprints->data[i];1210 if (pmFootprintCullPeaks(img, weight, fp, nsigma_delta, min_threshold) != PS_ERR_NONE) {1211 return psError(PS_ERR_UNKNOWN, false, "Culling pmFootprint %d", fp->id);1212 }1213 }1214 1215 return PS_ERR_NONE;1216 }1217 1218 /************************************************************************************************************/1219 142 /* 1220 143 * Extract the peaks in a psArray of pmFootprints, returning a psArray of pmPeaks … … 1242 165 } 1243 166 1244 void pmDetectionsFree (pmDetections *detections) {1245 1246 if (!detections) return;1247 1248 psFree (detections->footprints);1249 psFree (detections->peaks);1250 psFree (detections->oldPeaks);1251 return;1252 }1253 1254 // generate a pmDetections container with empty (allocated) footprints and peaks containers1255 pmDetections *pmDetectionsAlloc() {1256 1257 pmDetections *detections = (pmDetections *)psAlloc(sizeof(pmDetections));1258 psMemSetDeallocator(detections, (psFreeFunc) pmDetectionsFree);1259 1260 detections->footprints = NULL;1261 detections->peaks = NULL;1262 detections->oldPeaks = NULL;1263 detections->last = 0;1264 1265 return (detections);1266 }1267 1268 167 /************************************************************************************************************/ 1269 /*1270 * Cull a set of peaks contained in a psArray of pmFootprints1271 */1272 psErrorCode1273 psphotCullPeaks(const psImage *image, // the image wherein lives the footprint1274 const psImage *weight, // corresponding variance image1275 const psMetadata *recipe,1276 psArray *footprints) { // array of pmFootprints1277 bool status = false;1278 float nsigma_delta = psMetadataLookupF32(&status, recipe, "FOOTPRINT_CULL_NSIGMA_DELTA");1279 if (!status) {1280 nsigma_delta = 0; // min.1281 }1282 float nsigma_min = psMetadataLookupF32(&status, recipe, "FOOTPRINT_CULL_NSIGMA_MIN");1283 if (!status) {1284 nsigma_min = 0;1285 }1286 const float skyStdev = psMetadataLookupF32(NULL, recipe, "SKY_STDEV");1287 1288 return pmFootprintArrayCullPeaks(image, weight, footprints,1289 nsigma_delta, nsigma_min*skyStdev);1290 }1291 -
trunk/psphot/src/pmFootprint.h
r16820 r17443 30 30 int pmFootprintSetNpix(pmFootprint *fp); 31 31 void pmFootprintSetBBox(pmFootprint *fp); 32 psArray *pmFindFootprints(const psImage *img, const float threshold, const int npixMin); 33 pmFootprint *pmFindFootprintAtPoint(const psImage *img, 32 33 pmSpan *pmFootprintAddSpan(pmFootprint *fp, // the footprint to add to 34 const int y, // row to add 35 int x0, // range of 36 int x1); // columns 37 38 psArray *pmFootprintsFind(const psImage *img, const float threshold, const int npixMin); 39 pmFootprint *pmFootprintsFindAtPoint(const psImage *img, 34 40 const float threshold, 35 41 const psArray *peaks, 36 42 int row, int col); 37 43 38 psArray *pm GrowFootprintArray(const psArray *footprints, int r);39 psArray *pm MergeFootprintArrays(const psArray *footprints1, const psArray *footprints2,44 psArray *pmFootprintArrayGrow(const psArray *footprints, int r); 45 psArray *pmFootprintArraysMerge(const psArray *footprints1, const psArray *footprints2, 40 46 const int includePeaks); 41 47 42 48 psImage *pmSetFootprintArrayIDs(const psArray *footprints, const bool relativeIDs); 43 49 psImage *pmSetFootprintID(const pmFootprint *fp, const int id); 50 void pmSetFootprintArrayIDsForImage(psImage *idImage, 51 const psArray *footprints, // the footprints to insert 52 const bool relativeIDs); // show IDs starting at 0, not pmFootprint->id 44 53 45 psErrorCode pm PeaksAssignToFootprints(psArray *footprints, const psArray *peaks);54 psErrorCode pmFootprintsAssignPeaks(psArray *footprints, const psArray *peaks); 46 55 47 56 psErrorCode pmFootprintArrayCullPeaks(const psImage *img, const psImage *weight, psArray *footprints, -
trunk/psphot/src/psphot.h
r17396 r17443 49 49 50 50 // used by psphotFindDetections 51 psArray *psphotFindPeaks (pmReadout *readout, psMetadata *recipe, const bool returnFootprints, const int pass, psMaskType maskVal); 51 psImage *psphotSignificanceImage (pmReadout *readout, psMetadata *recipe, const int pass, psMaskType maskVal); 52 psArray *psphotFindPeaks (psImage *significance, pmReadout *readout, psMetadata *recipe, const int pass, psMaskType maskVal); 53 bool psphotFindFootprints (pmDetections *detections, psImage *significance, pmReadout *readout, psMetadata *recipe, const int pass, psMaskType maskVal); 52 54 psErrorCode psphotCullPeaks(const psImage *img, const psImage *weight, const psMetadata *recipe, psArray *footprints); 53 55
Note:
See TracChangeset
for help on using the changeset viewer.
