|
How the Frames Pipeline Sets its FlagsPhoto version $Name: $The frames pipeline uses a set of flags for each object to call attention to possible problems and to record decisions made during processing. These flags apply both to the object as a whole (including data from each band), and to each band separately.
An Reading the following description will be easier if you have a nodding acquaintance with the frames pipeline, but it should be useful to all.
The Flags that Frames UsesAs of version V4_8, photo has two sets of flags,flags
and flags2 . There is no essential difference between the
two; the first 32 bits if information are set in flags
and the remainder in flags2 . Flags in the former have
names beginning OBJECT1_ ,
in the latter they begin
OBJECT2_ .
OBJECT2 flags:
When and Where Flags are Set as Frames RunsThe variable objc refers to anOBJC ,
while object1 refers to an OBJECT1 .
You are expected to imagine a loop over all filters, setting flags
as appropriate.
The following pseudo-code roughly corresponds to the organisation of the frames pipeline, and bears some resemblence to C. The following idioms are used:
The numbers in the right hand margin refer to the previous section, where the individual flags are described. find_objects { if(pixels over threshold touch edge of frame) { object1->flags |= OBJECT1_EDGE; [2] } if(object includes unsearched pixels (e.g. unsmoothed edges of frames)) { object1->flags |= OBJECT1_NOTCHECKED; [19] reject all peaks in NOTCHECKED parts of frame; } /* * information about which mode object was detected in */ if(bright objects) { object1->flags |= OBJECT1_BRIGHT; [1] } if(not binned) { object1->flags |= OBJECT1_BINNED1; [28] } else if(binned 2x2) { object1->flags |= OBJECT1_BINNED2; [29] } else if(binned 4x4) { object1->flags |= OBJECT1_BINNED4; [30] } /* * information about pixels contained in object */ if(object contains interpolated pixels) { object1->flags |= OBJECT1_INTERP; [17] if(object contains cosmic ray contaminated pixels) { object1->flags |= OBJECT1_CR; [12] } if(object contains saturated pixels after centering on peak) { object1->flags |= OBJECT1_SATUR; [18] } } } find_centre { if((object1->flags & OBJECT1_SATUR) && saturated star centroider fails) { [18] object1->flags |= OBJECT1_PEAKCENTER; [5] } for(;;) { find_peak_centre; if(centre is OK && width estimate is consistent with size of object) { break; } if(too close to edge while searching for peak) { object1->flags |= OBJECT1_EDGE | OBJECT1_PEAKCENTER; [2,5] break; } if(object has vanishing second derviative || centroider fails otherwise) { object1->flags |= OBJECT1_PEAKCENTER; [5] break; } bin image in row/and or column; } if(object1->flags & OBJECT1_PEAKCENTER) { [5] take centre of brightest pixel as peak position; } } merge_colors { merge together peaks in saturated cores; if(more than one peaks is detected within an object in one band) { objc->flags |= OBJECT1_BLENDED; [3] } if(distinct peaks found in different bands of same object) { objc->flags |= OBJECT1_BLENDED; [3] } if(multiple distinct peaks in merged objects) { objc->flags |= OBJECT1_BLENDED; [3] } if(obj1 and obj2 are to be merged) { if((!(obj1->flags & OBJECT1_DETECTED) && (obj2->flags & OBJECT1_DETECTED)) || [32] (obj1->flags & OBJECT1_CANONICAL_CENTER)) { [0] use obj2; } else if(((obj1->flags & OBJECT1_DETECTED) && !(obj2->flags & OBJECT1_DETECTED)) || [32] (obj2->flags & OBJECT1_CANONICAL_CENTER)) { [0] use obj1; } else { use the one with the brighter peak; } } } subtract_bright_stars { if(object1->flags & OBJECT1_EDGE) { [2] don't subtract wings; return; } if(object1->flags & OBJECT1_SATUR) { [18] estimate psfCounts from profile; if(more than 20% of counts are interpolated) { object1->flags2 |= OBJECT2_PSF_FLUX_INTERP; [15] } } if(object contains pixels from which star wings were subtracted) { object1->flags |= OBJECT1_SUBTRACTED; [20] } if(object is in part of frame where bright star wings exceeded 10 sky sigma, or star wings increased variance by more than 50%) { object1->flags |= OBJECT1_NOTCHECKED; [19] } } peak_up_astrometry { !(object1 & (OBJECT1_CANONICAL_CENTER | [0] OBJECT1_PEAKCENTER | [5] if(!(object1->flags & OBJECT1_DETECTED) || [32] (object1->flags & (OBJECT1_SATUR | OBJECT1_PEAKCENTER))) { [18][5] Don't use star in matchup } } measure_objects { /* * book keeping */ if(detected as a bright object) { if(measuring bright objects && !(objc->flags2 & OBJECT2_MEASURE_BRIGHT)) { [1] don't measure object; return; } if(being remeasured after faint object detection) { make new object which is sibling of bright object, and prepare to measure this new creation; unset OBJECT1_BRIGHT in objc and object1; [1] } } recentroid_and_find_canonical_centre { foreach object1 in objc { if(objc->flags2 & OBJECT2_DEBLENDED_AS_MOVING) { [0] we already have a good centre; continue; } if(!(detected in this band)) { if(!child) continue; } set centre from objc; object1->flags |= OBJECT1_CANONICAL_CENTER | OBJECT1_BINNED1; [0,28] object1->flags2 |= OBJECT2_DEBLEND_NOPEAK; [14] } } find_canonical_centre { object1 = object1 in canonical band; if(object1->flags & OBJECT1_DETECTED && [32] !(object1->flags) & OBJECT1_SATUR) { [18] use this object1's centre; } else if(at least one peak is non-saturated) { use brightest non-saturated peak; } else { use brightest peak; } convert to canonical band's coordinate system; } } create_object1s { foreach (missing OBJECT1) { make object1; if(bright) { object1->flags |= OBJECT1_BRIGHT; [1] } set centre from OBJC; object1->flags |= OBJECT1_CANONICAL_CENTER; [0] } if(blended) { objc->flags |= object1->flags & OBJECT1_BLENDED; [3] } } if(!(objc->flags & OBJECT1_BRIGHT)) { [1] save atlas images; } /* * set sky level */ if(object1->flags & OBJECT1_CHILD) { [4] sky += contribution from siblings; if(sky summation failed && child->flags2 & (OBJECT2_DEBLENDED_AT_EDGE | [13] OBJECT2_DEBLENDED_AS_MOVING)) { [0] sky = sky level at child's centre; } else { This cannot happen, so die; } } /* * extract profile; */ if(failed to extract radial profile) { object1->flags |= OBJECT1_EDGE | OBJECT1_NOPETRO | OBJECT1_NOPROFILE; object1->flags2 |= OBJECT2_LOCAL_EDGE; [7] give up measuring object in this band; [2,7,8] } if(pixel overflow while extracting radial profile) { object1->flags |= OBJECT1_SATUR; [18] } if(object is still detected at edge of extracted profile (about 260arcsec)){ object1->flags |= OBJECT1_TOO_LARGE; [24] } if(measured profile includes points with a S/N <= 0) { object1->flags |= OBJECT1_BAD_RADIAL; [15] } if(central value of object more than 100 sigma below sky level) { object1->flags |= OBJECT1_BADSKY | OBJECT1_NOPETRO; [8,22] use fallback value for "Petrosian" radius; give up measuring object in this band; something is horribly wrong. } if(radial profile has fewer than two points) { object1->flags |= OBJECT1_NOPROFILE; [7] object1->flags |= OBJECT1_NOPETRO | OBJECT1_ELLIPFAINT | OBJECT1_NOSTOKES; give up measuring Petrosian quantities, Stokes parameters, and isophotal quantities; [8,21,27] } /* * measure a few misc quantities */ if(central surface brightness is below threshold for isophotal shape || failed to fit ellipse at threshold +- delta) { object1->flags |= OBJECT1_ELLIPFAINT; [27] give up fitting ellipse; } if(failed to measure U or Q due to numerical difficulties) { object1->flags |= OBJECT1_NOSTOKES; [21] } /* * measure Petrosian quantities */ while(surface brightness at innermost candidate for Petrosian radius is too low) { reject candidate; object1->flags |= OBJECT1_PETROFAINT; [23] } if(there are no Petrosian radii) { object1->flags |= OBJECT1_NOPETRO; [8] if(we didn't reject any candidates for the Petrosian radius) { object1->flags |= OBJECT1_NOPETRO_BIG; [10] use outermost point in radial profile as "Petrosian" radius; } else { use fallback value for "Petrosian" radius; } } else { if(there is more than one Petrosian radius) { object1->flags |= OBJECT1_MANYPETRO; [9] } } if(more than one value of Petrosian 50% light radius) { object1->flags |= OBJECT1_MANYR50; /* how can this happen? */ [13] } if(more than one value of Petrosian 90% light radius) { object1->flags |= OBJECT1_MANYR90; /* how can this happen? */ [14] } if(object1->flags & OBJECT1_NOPETRO) { [8] give up on errors for Petrosian quantities; } if(object is less than one (r') Petrosian radius from edge of frame) { object1->flags |= OBJECT1_INCOMPLETE_PROFILE; [16] } /* * Measure fibre and psf counts */ foreach type (fibre psf) { if(counts were contaminated by INTERP pixels) { [17] estimate errors from "real" pixels if(contamination exceeded 50% || (obj1->flags2 & OBJECT2_BAD_COUNTS_ERROR)) { [8] obj1->flags2 |= OBJECT2_BAD_COUNTS_ERROR; [8] } } } /* * Measure quantities about the centre determined in this band */ if(local band is to close to the edge of the frame) { object1->flags |= OBJECT1_CANONICAL_CENTER | OBJECT1_EDGE; [0][2] object1->flags2 |= OBJECT2_LOCAL_EDGE; [7] use (transformed) canonical centre; } /* * mark measured as a bright object */ if(measuring bright objects) { objc->flags |= OBJECT1_BRIGHT; [1] } /* * If object is a child and a blend, note that we don't (yet) run * deblender recursively. Moving objects appear blended, but they * aren't, so handle that case too */ if((objc->flags & OBJECT1_CHILD) & (objc->flags & OBJECT1_BLENDED)) { [43] if(objc->flags2 & OBJECT2_DEBLENDED_AS_MOVING) { [0] objc->flags &= ~OBJECT1_BLENDED; [3] } else { objc->flags |= OBJECT1_NODEBLEND; [6] } } /* * Flag problems near the object's centre */ if(centre of object is within 3 pixels of an interpolated pixel) { objc->flags2 &= OBJECT2_INTERP_CENTER; [12] } if(centre of object is within 3 pixels of a saturated pixel) { objc->flags2 &= OBJECT2_SATUR_CENTER; [11] } /* * Classify object as e.g. star/galaxy */ if(object1->flags2 & OBJECT2_INTERP_CENTER) { [12] object1->type = unknown; } if(!(object1->flags & OBJECT1_DETECTED)) || [32] object1->flags2 & (OBJECT2_DEBLEND_NOPEAK | OBJECT2_INTERP_CENTER)) { [14,12] don't consider this band in objc's classification; } /* * propagate flags to and from OBJC */ if(objc->flags & OBJECT1_BLENDED) { [3] object1->flags |= OBJECT1_BLENDED; [3] } objc->flags |= object1->flags & (OBJECT1_EDGE | [2] OBJECT1_BLENDED | [3] OBJECT1_CHILD | [4] OBJECT1_NOPETRO | [8] OBJECT1_MANYPETRO | [9] OBJECT1_INTERP | [17] OBJECT1_CR | [12] OBJECT1_SATUR | [18] OBJECT1_NOTCHECKED | [19] OBJECT1_BINNED1 | [28] OBJECT1_BINNED2 | [29] OBJECT1_BINNED4); [30] objc->flags2 &= object1->flags2 & (OBJECT2_DEBLENDED_AS_MOVING | [1] OBJECT2_NODEBLEND_MOVING | [1] OBJECT2_TOO_FEW_DETECTIONS | [2] OBJECT2_BAD_MOVING_FIT | [3] OBJECT2_STATIONARY | [4] OBJECT2_PEAKS_TOO_CLOSE | [5] OBJECT2_BAD_MOVING_FIT_CHILD | [9] OBJECT2_DEBLEND_UNASSIGNED_FLUX | [10] OBJECT2_SATUR_CENTER | [12] OBJECT2_INTERP_CENTER | [11] OBJECT2_DEBLENDED_AT_EDGE | [13] OBJECT2_DEBLEND_NOPEAK); [14] } find_velocity { if(objc->flags2 & OBJECT2_DEBLENDED_AS_MOVING) { [0] we already have the velocity; return; } if(((object1->flags & OBJECT1_DETECTED) && [32] !(object1->flags2 & (OBJECT2_DEBLEND_NOPEAK | [14] OBJECT2_INTERP_CENTER)) && [12] !(object1->flags & (OBJECT1_CANONICAL_CENTER | [0] OBJECT1_PEAKCENTER | [5] OBJECT2_INTERP_CENTER)) { [12] use centre in this band in velocity fit } if(chisq was too large) { objc->flags |= OBJECT2_BAD_MOVING_FIT; [3] } } deblender { if(measuring bright objects && OBJECT1_BLENDED) { [3] objc->flags |= OBJECT1_NODEBLEND; [6] give up on the object as it'll be reprocessed !BRIGHT; } if(there are too many peaks in object) { objc->flags |= OBJECT1_DEBLEND_TOO_MANY_PEAKS; [11] only use the n brightest peaks; } if(peaks are too close together) { forget the fainter peak; objc->flags |= OBJECT2_PEAKS_TOO_CLOSE; [5] } if(there's an "object" found at a different place in each band) { if(enough peaks in significantly different places) { objc->flags |= OBJECT1_MOVED; [31] } } if(objc->flags & OBJECT1_MOVED) { [31] objc->flags2 |= OBJECT2_DEBLENDED_AS_MOVING; [0] create extra MOVED child; child->flags |= OBJECT1_MOVED; [31] child->flags2 |= OBJECT2_DEBLENDED_AS_MOVING; [0] } foreach(merged peak found in parent) { make_new_child_from_objc { if(child is detected in <= deblend_min_detect bands) { objc->parent->flags2 |= OBJECT2_TOO_FEW_DETECTIONS; [2] don't create child; continue; } if(peak is lablelled as MOVED) { child->flags |= OBJECT1_MOVED; [31] } child->flags = objc->flags & (OBJECT1_EDGE | [2] OBJECT1_PEAKCENTER | [5] OBJECT1_INTERP | [17] OBJECT1_NOTCHECKED | [19] OBJECT1_SUBTRACTED); [20] child->flags |= OBJECT1_CHILD; [4] child_object1->flags = object1->flags & (OBJECT1_CANONICAL_CENTER | [0] OBJECT1_EDGE | [2] OBJECT1_PEAKCENTER | [5] OBJECT1_NOTCHECKED | [19] OBJECT1_SUBTRACTED); [20] child_object1->flags |= OBJECT1_CHILD; [4] if(a peak was found in this band) { set centre from peak; child_object1->flags |= OBJECT1_BINNED1; [28] } else { set centre from OBJC; child_object1->flags |= OBJECT1_CANONICAL_CENTER; [0] } } } phObjcDeblend { if(objc will be OBJECT1_EDGE in next strip/field too) { [2] objc->flags2 |= OBJECT2_DEBLENDED_AT_EDGE; [13] child->flags2 |= OBJECT2_DEBLENDED_AT_EDGE; [13] trim the part of objc's atlas image that's too close to edge of field; } else { if(objc->flags & OBJECT1_EDGE) { [2] objc->flags |= OBJECT1_NODEBLEND; [6] give up, as edge objects invalidate assumptions made by deblender, and will be deblended in next strip/field anywayl } if(objc->flags & OBJECT1_MOVED) { [31] find child with (child->flags2 & OBJECT2_DEBLENDED_AS_MOVING); [0] if(phObjcDeblendMovingChild(moving_child) succeeds) { delete all children with OBJECT1_MOVED set, as we have decided [31] to treat them as part of their own moving object; } } deblend_template_find { if(!(objc->flags & OBJECT1_SATUR) && [18] child is consistent with PSF) { Subtract PSF from parent and use PSF as template in this band; objc_object1->flags |= OBJECT1_DEBLENDED_AS_PSF; [25] } if(template for this child is too large (more than half a frame)) { objc->parent->flags |= OBJECT1_TOO_LARGE | OBJECT1_NODEBLEND;[6,24] give up on deblending parent; } if(template + (smoothing length)/2 hangs over edge of frame in any band) { objc->parent->flags |= OBJECT1_EDGE | OBJECT1_NODEBLEND; [2,6] give up on deblending parent; } find templates for child; if(we wouldn't have detected this child in any band) { delete child; } if(failed to find a template in this band) { object1->flags &= ~OBJECT1_DETECTED; [32] object1->flags |= OBJECT1_DEBLENDED_AS_PSF; [25] use PSF template; } objc->flags |= object1->flags & OBJECT1_DETECTED; [32] } setup Normal Equations, and solve for weights for each child; if(at least one possible child is rejected due to singular matrix) { objc->flags |= OBJECT1_DEBLEND_PRUNED; [26] object1->flags |= OBJECT1_DEBLEND_PRUNED; [26] } if(only one child remains) { objc->flags &= ~OBJECT1_BLENDED; [3] ensure that object's peaks correspond to those in the child, with OBJECT1_CANONICAL_CENTER and OBJECT1_DETECTED set correctly; [0,[32] no need to deblend; } if(object is detectable in this band, but wasn't seen due (usually) to the vaguaries of peak matching) { object1->flags |= OBJECT1_BINNED1; [28] } if(Petrosian flux in children> deblend_allowed_unassigned*(parent's petroCounts)) { objc->color[c]->flags2 |= OBJECT2_DEBLEND_UNASSIGNED_FLUX; [10] } if(child includes CR pixels) { object1->flags |= OBJECT1_CR; [12] } if(child includes interpolated pixels) { object1->flags |= OBJECT1_INTERP; [17] } if(child includes saturated pixels) { object1->flags |= OBJECT1_SATUR; [18] } } } phObjcDeblendMovingChild { if(object is detected in <= 2 bands) { objc->parent->flags2 |= (OBJECT2_TOO_FEW_DETECTIONS | [2] OBJECT2_NODEBLEND_MOVING); [1] objc->parent->flags2 &= ~OBJECT2_DEBLENDED_AS_MOVING; [0] } if(chi^2 for velocity fit is too large) { objc->parent->flags2 |= (OBJECT2_BAD_MOVING_FIT_CHILD | [9] OBJECT2_NODEBLEND_MOVING); [1] objc->parent->flags2 &= ~OBJECT2_DEBLENDED_AS_MOVING; [0] } if(velocity is consistent with 0) { objc->parent->flags2 |= (OBJECT2_STATIONARY | [4] OBJECT2_NODEBLEND_MOVING); [1] objc->parent->flags2 &= ~OBJECT2_DEBLENDED_AS_MOVING; [0] } if(deblended as moving) { set centres for all OBJECT1_CANONICAL_CENTER objects [0] from the fit to the object's motion; } } |