|
||||||||||||||||||||||
|
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;
}
}
| |||||||||||||||||||||